@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/marketing-consent.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 * The READ side of marketing consent (`docs/specs/email-overhaul.md` §3f).\n *\n * `marketingConsent` has seven writers and, until this module, no reader on\n * any send path: `performCampaignSend` filtered on the suppression list and\n * on nothing else, so a recorded opt-in and a recorded opt-OUT reached the\n * same inbox. This is the join that makes the field mean something.\n *\n * ## A basis is DECLARED, never inferred\n *\n * Nothing here treats an act as consent. Submitting a form, buying, booking,\n * or holding an account are all things a person did for their own reasons,\n * and none of them is an opt-in to marketing email. The only input is a\n * stored `marketingConsent` written from a checkbox the person ticked. That\n * is why {@link readMarketingBasis} takes a document rather than a silo name:\n * there is no silo whose mere membership grants a basis.\n *\n * ## Three states, not two\n *\n * The field is written `true` by six capture paths and `false` by exactly one\n * (`POST /v1/contacts`), so absence is not refusal — it is the far commoner\n * case of a record captured before the checkbox existed. Collapsing absent\n * into refused is what would empty an existing audience overnight; collapsing\n * it into granted is what the product does today. Both are wrong, so the\n * split is carried through to the caller as three counts rather than a\n * boolean.\n *\n * ## A basis also records WHOSE act it was\n *\n * A basis an operator asserted over existing records — see\n * `tools/scripts/backfill-marketing-consent.mjs` — is mailable, because an\n * org that asserted it meant that data to be reachable. It is not, however,\n * the same fact as a checkbox somebody ticked, and two fields that store\n * only `true` and a date cannot tell the difference. So an asserted basis\n * carries {@link MARKETING_CONSENT_SOURCE_FIELD} naming who asserted it,\n * when, and why, and every reader gets that distinction back as\n * {@link MarketingConsentRecord.assertedBy}. Consent is only evidence for as\n * long as its origin survives with it.\n *\n * ## A basis also records WHICH BRAND it was given to\n *\n * Contacts are ORG-scoped and shared across every site in the org, which is\n * deliberate: an agency running many client brands out of one account keeps\n * one address book. Consent is not shareable on the same terms. It runs to a\n * controller — the brand named on the form, in the sender line and in the\n * privacy notice the person read — and not to the account that happens to\n * hold the row. One boolean per person cannot express that, so somebody who\n * opted in to one client's newsletter was mailable by every other client in\n * the agency, on a basis none of them was given.\n *\n * So a basis is stored per (person, host), under\n * {@link MARKETING_CONSENT_BY_HOST_FIELD}, and {@link readMarketingBasis}\n * takes the host it is being read FOR. The host argument is required and has\n * no default: a reader that has not said which brand is asking cannot\n * compile, which is the same guarantee `newResourceScopeFields` gives on the\n * write side of scoped resources.\n *\n * ## FIVE states, because collapsing any two of them is a bug in one\n * direction or the other\n *\n * \"This host has no basis for this person\" and \"another host has one\" and \"a\n * grant exists that names no host at all\" and \"this person refused\" are\n * different facts, and every pair of them collapses into either a leak or a\n * silent failure to mail somebody who did opt in. They are carried apart all\n * the way to the caller as {@link MarketingConsentReason}, and\n * {@link MarketingConsentSplit} counts them separately rather than reporting\n * one `withheld` total that cannot be explained to a merchant.\n *\n * ## A REFUSAL over-applies; a GRANT never does\n *\n * The pre-host field is still read, and asymmetrically. A stored\n * `marketingConsent: false` refuses for every host, because a refusal\n * recorded against nobody in particular is most safely honored against\n * everybody, and withholding mail is recoverable where sending it is not. A\n * stored `marketingConsent: true` grants to NO host: it is a claim with no\n * controller attached, and spreading it across an agency's unrelated clients\n * is precisely the defect this module exists to end. Such a record reports\n * {@link MarketingConsentRecord.otherGrant} as `'unscoped'` so a console can\n * say why somebody stopped being mailable, and so\n * `tools/scripts/backfill-consent-host.mjs` can find them.\n */\n\nimport {\n type ConsentGroup,\n soloConsentGroup,\n} from './consent-groups'\n\n/**\n * The document field carrying WHO asserted a stored basis, and why.\n *\n * `marketingConsent` on its own records only WHAT the basis is. A record\n * written by an operator and a record written from a checkbox a person\n * ticked are the same two fields with the same two values, so nothing\n * downstream — a console readout, an export, a regulator's question — can\n * tell them apart. That is the difference between consent that is evidence\n * and consent that is an assertion, so it is carried on the record itself\n * rather than left to be reconstructed from when a script happened to run.\n *\n * Absent on every record written from a site's capture surface, which is why\n * {@link readMarketingBasis} reads its absence as the person's own act: the\n * six checkbox writers are the norm and an operator assertion is the thing\n * that has to announce itself. The platform's own console doors (AGL-3185)\n * do write it — which door, which wording version — and say whose act it\n * describes with {@link MarketingConsentSource.actor}, so a record can carry\n * provenance without being read as an assertion made on the person's behalf.\n */\nexport const MARKETING_CONSENT_SOURCE_FIELD = 'marketingConsentSource'\n\n/**\n * The `kind` an operator backfill stamps. A named constant because the\n * script that writes it and the reader that finds it are in different\n * languages and cannot share a type.\n */\nexport const OPERATOR_BACKFILL_CONSENT_KIND = 'operator-backfill'\n\n/**\n * The document field a LIST MEMBERSHIP carries beside `marketingConsent`,\n * naming which of the two ways in produced it.\n *\n * `list-members.ts` writes it, with `'contact-opt-in'` for a basis carried\n * across from the person's own record and\n * {@link OPERATOR_ATTESTED_CONSENT_BASIS} for one a console account asserted.\n * It is read here because a membership is a person record like any other to\n * every reader in the send path, and a reader that knew only about\n * {@link MARKETING_CONSENT_SOURCE_FIELD} would report an attestation as the\n * person's own act.\n */\nexport const MARKETING_CONSENT_BASIS_FIELD = 'marketingConsentBasis'\n\n/**\n * The value of {@link MARKETING_CONSENT_BASIS_FIELD} that means an operator\n * stated they have this person's permission.\n *\n * A string literal shared by the writer and the reader rather than a type,\n * for the reason above: the enrollment path stores this on a document and\n * this module reads it back off one, and a document has no types.\n */\nexport const OPERATOR_ATTESTED_CONSENT_BASIS = 'operator-attested'\n\n/**\n * The `kind` synthesized for an attested membership that carries no\n * {@link MARKETING_CONSENT_SOURCE_FIELD} of its own.\n *\n * The console's one-address add path stores the attesting account in\n * `marketingConsentByUid` and no provenance object, so the provenance a\n * reader can honestly report for those rows is exactly \"an account attested\n * this\" plus the account. Naming the kind rather than leaving the source\n * `null` is what lets a console readout say WHO, which is the whole reason\n * the attribution is stored.\n */\nexport const OPERATOR_ATTESTED_CONSENT_KIND = 'operator-attested'\n\n/**\n * The boolean field a basis has always been stored in.\n *\n * Named rather than spelled out at each site because it now appears in two\n * positions — at the top of a person document, and again inside every entry\n * of {@link MARKETING_CONSENT_BY_HOST_FIELD} — and the second position is\n * only readable by the same parser as the first while both agree on the name.\n */\nexport const MARKETING_CONSENT_FIELD = 'marketingConsent'\n\n/**\n * The map field holding one consent entry per host: `{ [hostId]: entry }`,\n * where an entry carries exactly the fields the top level carries.\n *\n * ## Why a map on the person document and not a subcollection\n *\n * The send path reads consent out of the audience sweep, while the person's\n * document is already in hand — that placement is what keeps the consent join\n * free. A subcollection would make it one extra read PER RECIPIENT PER\n * CAMPAIGN, on the one path in the product whose cost scales with the size of\n * a customer's list. The map rides along with a document that is being read\n * anyway.\n *\n * ## Why keyed by host id\n *\n * `entry = map[hostId]` is a lookup that has no way to return another host's\n * grant. A list of entries carrying a `hostId` field would have to be\n * searched, and a search is something a caller can get wrong — a `.find()`\n * whose predicate is dropped still returns a grant, just somebody else's.\n * There is no expression of this shape that reads host B's consent while\n * asking about host A.\n *\n * ## Why the entries repeat the top-level field names\n *\n * So that one parser reads both. Provenance, the attested-basis marker, the\n * attesting account and the timestamp all mean the same thing wherever they\n * sit, and a second set of names would be a second set of rules for the two\n * positions to disagree about — which is how the attested-membership case\n * came to be reported as a person's own act in the first place.\n */\nexport const MARKETING_CONSENT_BY_HOST_FIELD = 'marketingConsentByHost'\n\n/**\n * The field naming every host that has CAPTURED a person record.\n *\n * Not consent, and deliberately not stored with it. Somebody captured by one\n * brand may go on to consent to two, or to none; conflating capture with\n * permission is how an act a person took for their own reasons — booking a\n * table, buying a thing — comes to be read as an opt-in. It is here because a\n * grandfathered basis is a claim about WHEN a record was captured, and that\n * claim only ever belonged to the brands that captured it.\n *\n * ## An ARRAY, and top-level, because it has to be a QUERY\n *\n * It serves both organizations from one field. An agency filters to \"the\n * people MY site captured\"; a business running three sites as one asks for\n * \"everyone captured on A, B or C\". Both are `array-contains-any` over this\n * field, which is why it is a top-level array of ids and not a label on a\n * detail page and not a map: Firestore can filter this shape and cannot\n * filter an array of objects.\n *\n * It GROWS. A person who fills in a second site's form is captured by that\n * site too, and the write is an `arrayUnion` on the merge branch as well as\n * on the create — which is exactly what the create-only `hostId` beside it\n * could never record.\n *\n * Read with the scalar `hostId` as a fallback on `contacts`, which has\n * stamped its FIRST capturing host under that name since the collection\n * existed. That is a strict subset of this fact under an older name, and a\n * reader that ignored it would report every existing contact as unattributed.\n */\nexport const CAPTURED_BY_HOST_FIELD = 'capturedByHostIds'\n\n/**\n * The consent group a stored grant was given to, recorded ON THE ENTRY.\n *\n * The declaration lives on the org and can change; this is what was DISCLOSED\n * to this person at the moment they agreed. Reading pooling from the org's\n * current declaration instead would let adding a site to a group widen every\n * grant ever made, which is the leak with a configuration screen in front of\n * it. So the group travels with the grant, and a group that grows reaches\n * only the captures that come after it.\n */\nexport const CONSENT_GROUP_ID_FIELD = 'consentGroupId'\n\n/**\n * The group's display name as the capture surface showed it.\n *\n * Stored beside the id because the id is not evidence: answering \"what was\n * this person told\" with a document key is answering a different question.\n * A group can also be renamed, and the name that matters is the one on the\n * screen they were looking at.\n */\nexport const CONSENT_GROUP_NAME_FIELD = 'consentGroupName'\n\n/**\n * The provenance stored alongside a basis: who recorded it, when, and why.\n *\n * Written by the operator backfill and, since AGL-3185, by the platform's\n * own console doors for the person's own decision.\n */\nexport interface MarketingConsentSource {\n /**\n * What kind of record this is — {@link OPERATOR_BACKFILL_CONSENT_KIND}, or\n * the console door that captured a person's own decision.\n */\n kind: string\n /**\n * Who recorded it: the operator who asserted it, or the account that made\n * its own decision. Never blank on a well-formed record.\n */\n by: string\n /** When it was recorded. */\n atMs: number | null\n /** Why, in prose, for whoever audits this later. */\n reason: string\n /**\n * Whose act this provenance describes.\n *\n * Absent on every record the operator writers stamp, and read as\n * `'operator'`: an assertion is what provenance meant before a door\n * existed that records the person's own click WITH provenance. Such a door\n * writes `'person'`, which is the only way a record carrying this field\n * reads as the person's own act.\n */\n actor?: MarketingConsentAssertedBy\n /**\n * The version of the consent wording the person was shown, when the door\n * versions its wording. Absent on an operator assertion, which showed the\n * person nothing.\n */\n textVersion?: string\n}\n\n/** Whose act a stored basis represents. */\nexport type MarketingConsentAssertedBy = 'person' | 'operator'\n\n/** What a person's stored consent record says, if anything. */\nexport type MarketingBasis =\n /** A checkbox was ticked and stored. Mailable under every policy. */\n | 'granted'\n /** A stored refusal. Never mailable, under every policy. */\n | 'declined'\n /** No consent field on the record at all — see {@link MarketingConsentPolicy}. */\n | 'unrecorded'\n\n/**\n * A grant this host may not use, and which kind it is.\n *\n * Reported so that \"nobody has ever consented for this person\" and \"somebody\n * consented, to a brand that is not the one asking\" stay distinguishable. The\n * two produce the same verdict and must not produce the same explanation: the\n * first is a list that was never opted in, and the second is an agency's\n * client reaching for another client's audience.\n *\n * `'other-host'` outranks `'unscoped'` when both are true. Operationally they\n * are the same refusal, and the named host is the more useful half to show.\n */\nexport type MarketingConsentOtherGrant = 'none' | 'other-host' | 'unscoped'\n\n/** One recipient's consent facts, as read off whichever silo produced them. */\nexport interface MarketingConsentRecord {\n /**\n * The host this record was read FOR. Every field below answers about this\n * host and no other.\n *\n * Carried on the record rather than remembered by the caller because a\n * consent map is built in one place and consumed in another —\n * {@link splitByMarketingConsent} refuses a map whose records were read for\n * a different host, and it can only do that if the records say.\n */\n hostId: string\n /**\n * The consent group the read was made against — the declared set of sites\n * that are ONE sender, or the site alone when nothing was declared.\n *\n * A grant is looked up under {@link hostId} alone, because pooling is\n * written forward at capture. A REFUSAL is honored across every site in\n * this group, because a person who unsubscribed from one of three sites\n * that present as one sender has left the sender, and asking them to do it\n * twice more is both hostile and wrong.\n */\n groupId: string\n /**\n * A grant that exists but is not this host's — see\n * {@link MarketingConsentOtherGrant}. `'none'` when there is no such grant,\n * which is the only value under which an absent basis may grandfather.\n */\n otherGrant: MarketingConsentOtherGrant\n /**\n * Every host that captured the underlying person record; empty when none\n * is attributed.\n *\n * Attribution, not permission. It is read here for exactly one decision:\n * a record grandfathers on its capture date, and a date another brand\n * earned is not this brand's to rely on. An EMPTY list is the honest\n * unattributed case and grandfathers, for the reason a missing capture\n * DATE does — the unknown leans toward reachable.\n */\n capturedByHostIds: string[]\n /**\n * Whether any of {@link capturedByHostIds} is a member of the group this\n * record was read for.\n *\n * Derived at read time, because the raw list is a fact about the person and\n * this is a fact about the relationship — and only the reader holds the\n * group's membership. It is the input to grandfathering: the argument that\n * a record predates enforcement belongs to whoever collected the address,\n * and three sites declared as one sender collected it together.\n */\n capturedByGroup: boolean\n basis: MarketingBasis\n /**\n * Whose act the basis represents, or `null` when there is no basis to\n * attribute. `'person'` for the capture surfaces and for a console door\n * whose provenance says so, `'operator'` for a basis an operator asserted\n * on somebody's behalf.\n *\n * Not a second grade of mailability — {@link marketingConsentVerdict}\n * ignores it, because an org that asserted a basis for its own seed data\n * meant that data to be reachable. It exists so every surface that reports\n * consent can report which kind it has, instead of presenting an assertion\n * as evidence.\n */\n assertedBy: MarketingConsentAssertedBy | null\n /**\n * The provenance behind the basis; `null` when the writer stamped none,\n * which every site capture surface does. Present for an operator assertion\n * and for a console door's record of the person's own decision — the two\n * are told apart by {@link assertedBy}, not by this being set.\n */\n source: MarketingConsentSource | null\n /** When the basis was recorded, when the writer stamped it. */\n basisAtMs: number | null\n /**\n * When the underlying person record was captured, when the silo stores it.\n *\n * `null` is the honest answer for a hand-typed address, which has no record\n * behind it at all, and it is treated as \"captured before enforcement\" for\n * the reason given on {@link MarketingConsentPolicy.enforceFromMs}.\n */\n capturedAtMs: number | null\n}\n\n/**\n * How strictly `unrecorded` is treated. `granted` and `declined` are not\n * affected by this: a stored refusal is honored under every mode, which is\n * the one piece of enforcement that is safe to switch on with no decision\n * from anyone.\n */\nexport interface MarketingConsentPolicy {\n /**\n * `'forward'` — NOT retroactive. A record captured on or after\n * {@link enforceFromMs} needs a recorded basis; one captured before it\n * stays reachable and is reported as grandfathered. Turning this on drops\n * nobody who is currently in an audience, which is what makes it the mode\n * a deployment with a real back catalog wants.\n *\n * `'strict'` — retroactive. Every recipient needs a recorded basis, and\n * nothing grandfathers. It can shrink an existing audience sharply,\n * possibly to near zero.\n *\n * `'strict'` is the current default; see\n * {@link DEFAULT_MARKETING_CONSENT_POLICY} for the pre-release condition\n * that makes a retroactive default safe, and for when it stops being so.\n */\n mode: 'forward' | 'strict'\n /**\n * The forward cutoff, in epoch millis.\n *\n * A record with no capture timestamp is treated as captured BEFORE this,\n * because the silos that omit one are the older ones — the direction of the\n * unknown is toward grandfathering, so a missing field can only ever leave\n * somebody reachable, never silently withhold their mail.\n */\n enforceFromMs: number\n}\n\n/**\n * The default forward cutoff: 2026-09-01T00:00:00Z.\n *\n * A date rather than a per-org stamp, so the rule has an answer on an org that\n * has never opened the setting.\n *\n * It divides the audience only under `'forward'`, where a record predating it\n * grandfathers and every capture from the checkbox-bearing surfaces onward\n * carries a basis or is withheld. Under `'strict'` it decides nothing, since\n * no record grandfathers at any date.\n *\n * An org may move it later (a longer grace period) or earlier (voluntarily\n * enforcing over more of its own back catalog) through\n * {@link resolveMarketingConsentPolicy}.\n */\nexport const MARKETING_CONSENT_ENFORCED_FROM_MS = Date.UTC(2026, 8, 1)\n\n/**\n * The policy an org gets when it has never configured one.\n *\n * `'strict'` — every recipient needs a recorded basis, and nothing is\n * grandfathered. That is defensible because of WHEN it ships: the product is\n * pre-release with no real customers, so the population a retroactive rule\n * removes is test and seeded demo data. The usual objection to retroactive\n * enforcement — that it silently shrinks a live audience, sometimes to\n * nothing — has nobody here to injure.\n *\n * ⚠️ It stops being defensible the moment real addresses exist. A deployment\n * that has already collected an audience under the old rule sets `'forward'`\n * on its orgs rather than inheriting this, because flipping to strict over a\n * live back catalog withholds mail from people who were legitimately\n * reachable the day before.\n *\n * `enforceFromMs` stays populated and is still read: under `'strict'` it\n * decides nothing, but an org moving back to `'forward'` gets a real cutoff\n * rather than a missing field.\n */\nexport const DEFAULT_MARKETING_CONSENT_POLICY: MarketingConsentPolicy = {\n mode: 'strict',\n enforceFromMs: MARKETING_CONSENT_ENFORCED_FROM_MS,\n}\n\n/**\n * Reads an org's stored `marketingConsentPolicy` into a usable policy.\n *\n * Every unusable value falls back to the default rather than to \"off\": there\n * is no off. A malformed setting must not be a way to switch the join back\n * out, because the failure mode of that is mail to people who declined.\n *\n * The fallback is {@link DEFAULT_MARKETING_CONSENT_POLICY} itself rather than\n * a mode repeated here. This function is the ONLY path by which any send\n * reaches a policy, so a literal in it would be the real default and the\n * exported constant would be a decoration: changing the constant would move\n * what `marketing-consent.spec.ts` asserts and nothing that mails anybody.\n * Only the two modes a caller may actually store are honored, so a stored\n * value that means neither lands on the default with everything else.\n */\nexport function resolveMarketingConsentPolicy(\n stored: unknown,\n): MarketingConsentPolicy {\n const value = (stored ?? {}) as Record<string, unknown>\n const mode =\n value['mode'] === 'strict'\n ? 'strict'\n : value['mode'] === 'forward'\n ? 'forward'\n : DEFAULT_MARKETING_CONSENT_POLICY.mode\n const enforceFromMs = Number(value['enforceFromMs'])\n return {\n mode,\n enforceFromMs: Number.isFinite(enforceFromMs)\n ? enforceFromMs\n : MARKETING_CONSENT_ENFORCED_FROM_MS,\n }\n}\n\n/**\n * Coerces the several shapes a timestamp arrives in — epoch millis, a\n * Firestore `Timestamp`, a `Date` — to millis, or `null`.\n *\n * `toMillis` is duck-typed rather than imported: this module is in the\n * client-bundled barrel and must not pull in `firebase-admin`, and the two\n * SDKs' `Timestamp` classes are different types with the same method anyway.\n */\nfunction timestampMs(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 * Reads the consent facts off one person record from any silo.\n *\n * The field names differ by collection and the capture timestamp does too —\n * `contacts`, `leads` and `siteMembers` stamp `createdAt`, a list membership\n * stamps `addedAt` — so both are read from a small set of aliases rather than\n * one name per caller. A caller that has to remember which name its silo uses\n * is a caller that can read the wrong one and silently get `undefined`, which\n * is exactly how `siteMembers` came to be addressed by a blank merge tag.\n *\n * ## TWO fields can say \"an operator asserted this\", and both are read\n *\n * A backfilled CRM record announces itself with {@link\n * MARKETING_CONSENT_SOURCE_FIELD}; a LIST MEMBERSHIP announces itself with\n * {@link MARKETING_CONSENT_BASIS_FIELD} set to {@link\n * OPERATOR_ATTESTED_CONSENT_BASIS}, because the enrollment path stores what\n * KIND of basis a membership carries and stores the attesting account beside\n * it. Reading only the first would report every attested enrollment — every\n * address a merchant added by hand, and every address an import brings in —\n * as a checkbox the person ticked, which is precisely the conflation the\n * attribution exists to prevent. The send-time consent join reads a\n * membership through this function, so the wrong answer here is the wrong\n * answer in a compliance response.\n *\n * ## The basis comes from `hostId`'s ENTRY, and from nowhere else\n *\n * `entry = map[hostId]` — a lookup with no way to reach another host's grant.\n * The pre-host top-level field is consulted only for the asymmetry the module\n * note states: a `false` there refuses for every host, a `true` there grants\n * to none and reports itself as {@link MarketingConsentOtherGrant} `'unscoped'`\n * instead.\n *\n * @param record any silo's person document data, or `null` for an address\n * with no record behind it (a hand-typed manual audience).\n * @param hostId the site asking. Required and undefaulted: a caller that has\n * not said which brand wants to mail this person has not asked\n * a question this function can answer.\n */\nexport function readMarketingBasis(\n record: Record<string, unknown> | null | undefined,\n group: ConsentGroup,\n): MarketingConsentRecord {\n const { hostId } = group\n if (!record) {\n return {\n hostId,\n groupId: group.groupId,\n otherGrant: 'none',\n capturedByHostIds: [],\n capturedByGroup: false,\n basis: 'unrecorded',\n assertedBy: null,\n source: null,\n basisAtMs: null,\n capturedAtMs: null,\n }\n }\n const byHost = readConsentByHost(record)\n const entry = byHost[hostId] ?? null\n const capturedBy = readCapturedByHosts(record)\n /*\n * THE REFUSAL IS READ ACROSS THE WHOLE GROUP, AND THE GRANT IS NOT.\n *\n * A grant is written forward: at capture, every site the disclosure named\n * got its own entry, so `byHost[hostId]` is the complete answer and a group\n * that GROWS later cannot retroactively widen what somebody agreed to.\n *\n * A refusal has to run the other way. Three sites presenting as one sender\n * are one sender to the person unsubscribing from them, and making them do\n * it twice more is hostile and legally wrong; a site that JOINS the group\n * inherits the refusals already standing against its siblings. Reading the\n * current group is what delivers that, and it can only ever withhold.\n *\n * The pre-host top-level `false` is read the same way and for the same\n * reason: it names no controller, so it is honored against every one.\n */\n const refusedUnscoped = record[MARKETING_CONSENT_FIELD] === false\n const refusedInGroup = group.hostIds.find(\n (member) => byHost[member]?.[MARKETING_CONSENT_FIELD] === false,\n )\n const consent = entry?.[MARKETING_CONSENT_FIELD]\n const basis: MarketingBasis =\n refusedUnscoped || refusedInGroup !== undefined\n ? 'declined'\n : consent === true\n ? 'granted'\n : 'unrecorded'\n // Provenance is read off whichever position produced the basis, so a\n // refusal inherited from a sibling site or from the pre-host field is not\n // decorated with the attribution of some other host's grant.\n const fields = refusedUnscoped\n ? record\n : refusedInGroup !== undefined\n ? (byHost[refusedInGroup] ?? {})\n : (entry ?? {})\n const basisAtMs = timestampMs(fields['marketingConsentAtMs'])\n const source =\n readConsentSource(fields[MARKETING_CONSENT_SOURCE_FIELD]) ??\n readAttestedBasis(fields, basisAtMs)\n return {\n hostId,\n groupId: group.groupId,\n otherGrant: readOtherGrant(record, byHost, group),\n capturedByHostIds: capturedBy,\n capturedByGroup: capturedBy.some((id) => group.hostIds.includes(id)),\n basis,\n // A basis with no provenance is the person's own: the site capture\n // surfaces write nothing here, so absence is the norm and an operator\n // assertion is what has to be stated. Provenance that names the person\n // as its own actor is the one other way to be the person's act — a\n // console door records which door and which wording, and says so.\n // Attributing an absent basis to nobody keeps `unrecorded` from reading\n // as a person who declined to be attributed.\n assertedBy:\n basis === 'unrecorded'\n ? null\n : source !== null && source.actor !== 'person'\n ? 'operator'\n : 'person',\n source,\n basisAtMs,\n capturedAtMs:\n timestampMs(record['createdAt']) ?? timestampMs(record['addedAt']),\n }\n}\n\n/**\n * The per-host consent map, as a plain object of entries.\n *\n * Anything that is not an object of objects reads as empty rather than\n * throwing. A malformed map must degrade to \"no host has a basis\", which\n * withholds; degrading toward a grant would make a corrupt value a way to\n * mail people.\n */\nfunction readConsentByHost(\n record: Record<string, unknown>,\n): Record<string, Record<string, unknown>> {\n const raw = record[MARKETING_CONSENT_BY_HOST_FIELD]\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}\n const entries: Record<string, Record<string, unknown>> = {}\n for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n entries[key] = value as Record<string, unknown>\n }\n }\n return entries\n}\n\n/**\n * Which kind of grant this person holds that `hostId` may not use.\n *\n * Only GRANTS are reported. Another host's refusal is that host's preference\n * and says nothing about this one — treating it as a signal here would spread\n * an opt-out across an agency's unrelated clients, which is the same defect\n * as spreading an opt-in, pointed the other way.\n */\nfunction readOtherGrant(\n record: Record<string, unknown>,\n byHost: Record<string, Record<string, unknown>>,\n group: ConsentGroup,\n): MarketingConsentOtherGrant {\n for (const [key, entry] of Object.entries(byHost)) {\n if (group.hostIds.includes(key)) continue\n if (entry[MARKETING_CONSENT_FIELD] === true) return 'other-host'\n }\n return record[MARKETING_CONSENT_FIELD] === true ? 'unscoped' : 'none'\n}\n\n/**\n * The host that captured this record.\n *\n * {@link CAPTURED_BY_HOST_FIELD} first, then `hostId` — the older name for\n * the same fact on `contacts`. `leads` and `siteMembers` live under a host\n * and carry neither, which is why the absent case is a real answer rather\n * than an error: see {@link MarketingConsentReason} for what it decides.\n */\nfunction readCapturedByHosts(record: Record<string, unknown>): string[] {\n const found = new Set<string>()\n const array = record[CAPTURED_BY_HOST_FIELD]\n if (Array.isArray(array)) {\n for (const value of array) {\n if (typeof value === 'string' && value) found.add(value)\n }\n }\n // The older scalar name on `contacts`, which holds the FIRST capturing\n // site. A subset of the same fact, so it is unioned rather than preferred.\n const legacy = record['hostId']\n if (typeof legacy === 'string' && legacy) found.add(legacy)\n return [...found]\n}\n\n/**\n * Every host that holds a GRANT for this person, sorted.\n *\n * The console readout and the API projection both need it, and the agency\n * handoff is the third caller: \"which of these rows are this client's\" is\n * this list intersected with one host.\n *\n * A refusal is not reported here. The question is which brands may mail this\n * person, and an unscoped refusal answers it for all of them at once — see\n * {@link readMarketingBasis}, which is where that asymmetry is decided.\n */\nexport function marketingConsentHostIds(\n record: Record<string, unknown> | null | undefined,\n): string[] {\n if (!record || record[MARKETING_CONSENT_FIELD] === false) return []\n return Object.entries(readConsentByHost(record))\n .filter(([, entry]) => entry[MARKETING_CONSENT_FIELD] === true)\n .map(([hostId]) => hostId)\n .sort()\n}\n\n/**\n * The document fields that RECORD a basis for one host, as a spreadable\n * object to merge onto a person document.\n *\n * The one writer helper for eleven capture doors. They wrote\n * `{ marketingConsent: true, marketingConsentAtMs: … }` inline, eleven times,\n * and every one of those grants applied to every brand in the org. A helper\n * that takes the host is what makes the omission impossible: there is no\n * spelling of this call that records a grant without saying whose it is.\n *\n * ⛔ It writes a grant and never a refusal. An opt-out is not the inverse of\n * an opt-in — it belongs beside the site's suppression list, has to survive a\n * later capture that would otherwise overwrite it, and is recorded by\n * `declineMarketingConsentFields` instead.\n *\n * Nested rather than dot-pathed so it composes with a `{ merge: true }` set,\n * which deep-merges maps: writing one host's entry leaves every other host's\n * untouched. A dotted `update` would do the same but cannot be spread into\n * the object literals these doors already build, and cannot create the\n * document when it is new.\n *\n * @param atMs when the person gave it. Defaulted by the caller, not here: a\n * clock read hidden in a helper is one a test cannot pin.\n */\nexport function marketingConsentFieldsForGroup(\n group: ConsentGroup,\n atMs: number,\n extra?: Readonly<Record<string, unknown>>,\n): Record<string, unknown> {\n if (!group?.hostIds?.length) {\n throw new Error('[marketing-consent] a basis cannot be recorded for no host')\n }\n const entry = {\n [MARKETING_CONSENT_FIELD]: true,\n marketingConsentAtMs: atMs,\n /*\n * The disclosure, stored with the grant.\n *\n * A reader that resolved pooling from the org's CURRENT declaration would\n * let a site added to the group tomorrow inherit every grant made today,\n * on a disclosure those people never saw. Recording what was shown is\n * what makes pooling forward-only, and it is also the only honest answer\n * to \"what was this person told\" months later.\n */\n ...(group.declared\n ? {\n [CONSENT_GROUP_ID_FIELD]: group.groupId,\n [CONSENT_GROUP_NAME_FIELD]: group.name ?? '',\n }\n : {}),\n ...(extra ?? {}),\n }\n /*\n * ONE ENTRY PER DISCLOSED SITE, rather than one entry the reader has to\n * search for a group it belongs to.\n *\n * `byHost[hostId]` stays a lookup with no way to reach another controller's\n * grant, which is the guarantee the whole model rests on. A single entry\n * carrying a list of covered sites would make every read a search, and a\n * search is something a caller can get wrong in exactly one direction: a\n * dropped predicate still returns a grant, just somebody else's.\n */\n return {\n [MARKETING_CONSENT_BY_HOST_FIELD]: Object.fromEntries(\n group.hostIds.map((hostId) => [hostId, entry]),\n ),\n }\n}\n\n/**\n * The group-of-one convenience, for a writer that has a site and no org.\n *\n * Narrow by construction, which is the safe direction: a door that should\n * have pooled and did not withholds mail, where a door that pooled without a\n * disclosure sends it.\n */\nexport function marketingConsentFieldsForHost(\n hostId: string,\n atMs: number,\n extra?: Readonly<Record<string, unknown>>,\n): Record<string, unknown> {\n return marketingConsentFieldsForGroup(soloConsentGroup(hostId), atMs, extra)\n}\n\n/**\n * The document fields that record a REFUSAL for one host.\n *\n * Separate from the grant helper because the two are not one function with a\n * boolean. A refusal is written by the unsubscribe path and by the API's\n * explicit `marketingConsent: false`, neither of which carries provenance a\n * person supplied, and — unlike a grant — the pre-host field is still honored\n * against every host, so the two are not symmetric anywhere in this module.\n *\n * @param extra fields stored on the entry beside the refusal — the console\n * doors pass the person's own provenance here (AGL-3185), the\n * same way the grant helper takes it.\n */\nexport function declineMarketingConsentFields(\n hostId: string,\n atMs: number,\n extra?: Readonly<Record<string, unknown>>,\n): Record<string, unknown> {\n if (!hostId) {\n throw new Error('[marketing-consent] a refusal cannot be recorded for no host')\n }\n /*\n * Written against the ONE site the person acted on, and read across\n * whatever group that site is in at send time — see\n * {@link readMarketingBasis}. Denormalizing a refusal the way a grant is\n * denormalized would freeze it to the group as it stood, so a site joining\n * the group later would mail somebody who had already left the sender.\n */\n return {\n [MARKETING_CONSENT_BY_HOST_FIELD]: {\n [hostId]: {\n [MARKETING_CONSENT_FIELD]: false,\n marketingConsentAtMs: atMs,\n ...(extra ?? {}),\n },\n },\n }\n}\n\n/**\n * The provenance a list membership's own basis field carries, or `null`.\n *\n * `'contact-opt-in'` returns `null` deliberately and is not merely unhandled:\n * that value means the membership carries the PERSON's opt-in, passed through\n * with their own timestamp, so it is the strongest available statement that\n * nobody asserted anything on their behalf.\n *\n * `by` falls back to the empty string rather than refusing when the account\n * is missing. `readConsentSource` refuses an unattributed operator because\n * its field's only writer always fills one; here the fact being read is the\n * BASIS, which is unambiguous on its own, and losing the whole attestation\n * because the uid did not survive would report the row as a person's own\n * opt-in — the one direction this must never fail in.\n */\nfunction readAttestedBasis(\n record: Record<string, unknown>,\n basisAtMs: number | null,\n): MarketingConsentSource | null {\n if (record[MARKETING_CONSENT_BASIS_FIELD] !== OPERATOR_ATTESTED_CONSENT_BASIS)\n return null\n const by = record['marketingConsentByUid']\n return {\n kind: OPERATOR_ATTESTED_CONSENT_KIND,\n by: typeof by === 'string' ? by : '',\n atMs: basisAtMs,\n reason:\n typeof record['marketingConsentReason'] === 'string'\n ? (record['marketingConsentReason'] as string)\n : '',\n }\n}\n\n/**\n * Parses the provenance field, or `null` when there is none to parse.\n *\n * A malformed value reads as `null` — the person's own act — rather than as\n * a nameless operator assertion, because the only writer of this field is a\n * script that always fills `kind` and `by`. Inventing an unattributed\n * operator out of a corrupt value would put a claim in the audit trail that\n * nothing in the product ever made.\n */\nfunction readConsentSource(value: unknown): MarketingConsentSource | null {\n if (!value || typeof value !== 'object') return null\n const source = value as Record<string, unknown>\n const kind = typeof source['kind'] === 'string' ? source['kind'] : ''\n const by = typeof source['by'] === 'string' ? source['by'] : ''\n if (!kind || !by) return null\n // Only the two literals the type names; anything else reads as absent,\n // which is the operator default — a malformed actor must not turn an\n // assertion into the person's own act.\n const actor =\n source['actor'] === 'person' || source['actor'] === 'operator'\n ? source['actor']\n : undefined\n const textVersion =\n typeof source['textVersion'] === 'string' && source['textVersion']\n ? source['textVersion']\n : undefined\n return {\n kind,\n by,\n atMs: timestampMs(source['atMs']),\n reason: typeof source['reason'] === 'string' ? source['reason'] : '',\n ...(actor ? { actor } : {}),\n ...(textVersion ? { textVersion } : {}),\n }\n}\n\n/** What a policy decided about one recipient. */\nexport type MarketingConsentVerdict =\n /** A recorded basis. Mailable. */\n | 'consented'\n /** No recorded basis, captured before the cutoff. Mailable, and reported. */\n | 'grandfathered'\n /** Not mailable: a stored refusal, or no basis under an enforcing policy. */\n | 'withheld'\n\n/**\n * WHY a record landed on its verdict — the states that must not collapse.\n *\n * Three of these are `withheld`, and the whole point is that they are three.\n * A merchant told \"412 withheld\" cannot act; a merchant told which of these\n * 412 are their own un-opted-in list, which belong to a sister brand, and\n * which said no, can. And every pair of them, merged, is a defect: merge\n * `'other-host'` into `'granted'` and an agency's client mails another\n * client's subscribers; merge it into `'no-basis'` and nobody can see that\n * the leak was ever closed.\n */\nexport type MarketingConsentReason =\n /** This host holds a recorded basis. */\n | 'granted'\n /** No basis, captured before the cutoff by this host or by nobody. */\n | 'grandfathered'\n /** This host holds a recorded refusal, or an unscoped one does. */\n | 'declined'\n /** A grant exists, and it is not this host's. */\n | 'other-host'\n /** Nobody holds a basis for this person. */\n | 'no-basis'\n /** No basis, and the record's capture date falls under an enforcing rule. */\n | 'not-grandfathered'\n\n/** One record's verdict and the reason behind it. */\nexport interface MarketingConsentDecision {\n verdict: MarketingConsentVerdict\n reason: MarketingConsentReason\n}\n\n/**\n * Applies a policy to one record. Pure, and the only place the rule lives.\n *\n * ## Grandfathering is a claim about a CAPTURE, so it belongs to the capturer\n *\n * Under `'forward'`, a record with no basis stays reachable because it\n * predates enforcement. That argument is made on behalf of the brand that\n * collected the address — it had a relationship with this person before there\n * was a checkbox to tick. A sister brand in the same agency has no such\n * history and inherits none by sharing an address book, so a record captured\n * by another host does not grandfather here. Where no host is attributed the\n * argument has nobody to belong to and nobody to exclude, so it holds: the\n * unknown leans toward reachable, exactly as a missing capture DATE does.\n *\n * ## A grant elsewhere ends grandfathering outright\n *\n * `otherGrant` is evidence that consent WAS collected for this person and\n * that this host is not who it was given to. Grandfathering on top of that\n * would let the agency case back in through the one door the policy leaves\n * open, which is the same leak in a longer sentence.\n */\nexport function marketingConsentDecision(\n record: MarketingConsentRecord,\n policy: MarketingConsentPolicy,\n): MarketingConsentDecision {\n if (record.basis === 'granted') {\n return { verdict: 'consented', reason: 'granted' }\n }\n if (record.basis === 'declined') {\n return { verdict: 'withheld', reason: 'declined' }\n }\n if (record.otherGrant !== 'none') {\n return { verdict: 'withheld', reason: 'other-host' }\n }\n if (policy.mode === 'strict') {\n return { verdict: 'withheld', reason: 'no-basis' }\n }\n /*\n * A capture attributed only to sites outside this group. The\n * grandfathering argument belongs to whoever collected the address; a\n * sister brand inherits none of it by sharing an address book, while a\n * fellow member of a declared group collected it alongside. An EMPTY list\n * has nobody to belong to and nobody to exclude, so it falls through and\n * grandfathers — the same direction a missing capture date leans.\n */\n if (record.capturedByHostIds.length && !record.capturedByGroup) {\n return { verdict: 'withheld', reason: 'other-host' }\n }\n // `null` grandfathers: see MarketingConsentPolicy.enforceFromMs for why the\n // unknown leans toward reachable.\n if (record.capturedAtMs === null) {\n return { verdict: 'grandfathered', reason: 'grandfathered' }\n }\n return record.capturedAtMs >= policy.enforceFromMs\n ? { verdict: 'withheld', reason: 'not-grandfathered' }\n : { verdict: 'grandfathered', reason: 'grandfathered' }\n}\n\n/**\n * The verdict alone, for the callers that only decide whether to send.\n *\n * Delegated rather than reimplemented so there is one rule and not two that\n * agree today.\n */\nexport function marketingConsentVerdict(\n record: MarketingConsentRecord,\n policy: MarketingConsentPolicy,\n): MarketingConsentVerdict {\n return marketingConsentDecision(record, policy).verdict\n}\n\n/** The split of one audience, kept as counts so a caller can display it. */\nexport interface MarketingConsentSplit {\n /** The addresses that may actually be mailed, in the order given. */\n mailable: string[]\n /** Of `mailable`, how many carry a recorded basis. */\n consented: number\n /**\n * Of `consented`, how many hold a basis an OPERATOR asserted rather than\n * one the person gave — see {@link MarketingConsentRecord.assertedBy}.\n *\n * A subset of `consented` and not a fourth population, because it is not a\n * different mailability. It is reported separately so a surface showing\n * \"N consented\" can avoid presenting a backfill as N opt-ins.\n */\n consentedByOperator: number\n /** Of `mailable`, how many are reachable only because of grandfathering. */\n grandfathered: number\n /** Refused by the consent rule, and therefore never metered or mailed. */\n withheld: number\n /**\n * Of `withheld`, how many hold a recorded refusal.\n *\n * The three counts below partition `withheld` — they exist because one\n * total cannot be acted on. A merchant looking at a short audience needs to\n * know whether they are looking at people who said no, people nobody ever\n * asked, or people who opted in to a DIFFERENT brand in the same account\n * and were correctly left alone.\n */\n withheldDeclined: number\n /** Of `withheld`, how many hold a grant that belongs to another brand. */\n withheldOtherHost: number\n /** Of `withheld`, how many hold no basis anywhere. */\n withheldNoBasis: number\n}\n\n/**\n * Splits an audience into who may be mailed and who may not, FOR ONE HOST.\n *\n * Returns counts rather than addresses for the reported populations: the\n * console surface that shows this figure is telling a merchant how their\n * audience divides, and handing back the withheld ADDRESSES would turn a\n * consent readout into a way to export the people who declined.\n *\n * ## The host argument, and why a mismatch throws\n *\n * A consent map is built where the person documents are read and consumed\n * somewhere else. Between those two points the host can be lost — a helper\n * that takes one hostId and passes another, a cached map reused for a second\n * site, a merge of two audiences. The result would be host A's grants\n * deciding host B's send, silently and with every count still adding up.\n *\n * So the host is stated at both ends and compared. A mismatch is a wiring\n * defect that no runtime value can produce, and it fails loudly rather than\n * mailing somebody on a basis they gave to a different company. This is the\n * same refusal `newResourceScopeFields` makes over an empty scope, for the\n * same reason: a caller that computed the wrong thing is worth a stack trace.\n *\n * @param emails normalized, de-duplicated recipient addresses.\n * @param records consent facts by address, every one read for `hostId`; a\n * missing entry reads as a record-less address.\n * @param hostId the site sending. Every record must have been read for it.\n */\nexport function splitByMarketingConsent(\n emails: readonly string[],\n records: ReadonlyMap<string, MarketingConsentRecord>,\n policy: MarketingConsentPolicy,\n group: ConsentGroup,\n): MarketingConsentSplit {\n const { hostId } = group\n const split: MarketingConsentSplit = {\n mailable: [],\n consented: 0,\n consentedByOperator: 0,\n grandfathered: 0,\n withheld: 0,\n withheldDeclined: 0,\n withheldOtherHost: 0,\n withheldNoBasis: 0,\n }\n for (const email of emails) {\n const record = records.get(email) ?? readMarketingBasis(null, group)\n if (record.hostId !== hostId || record.groupId !== group.groupId) {\n throw new Error(\n '[marketing-consent] a consent record read for ' +\n `${record.hostId}/${record.groupId} cannot decide a send from ` +\n `${hostId}/${group.groupId}`,\n )\n }\n const decision = marketingConsentDecision(record, policy)\n switch (decision.verdict) {\n case 'consented':\n split.mailable.push(email)\n split.consented += 1\n if (record.assertedBy === 'operator') split.consentedByOperator += 1\n break\n case 'grandfathered':\n split.mailable.push(email)\n split.grandfathered += 1\n break\n default:\n split.withheld += 1\n if (decision.reason === 'declined') split.withheldDeclined += 1\n else if (decision.reason === 'other-host') split.withheldOtherHost += 1\n else split.withheldNoBasis += 1\n }\n }\n return split\n}\n"],"names":["soloConsentGroup","MARKETING_CONSENT_SOURCE_FIELD","OPERATOR_BACKFILL_CONSENT_KIND","MARKETING_CONSENT_BASIS_FIELD","OPERATOR_ATTESTED_CONSENT_BASIS","OPERATOR_ATTESTED_CONSENT_KIND","MARKETING_CONSENT_FIELD","MARKETING_CONSENT_BY_HOST_FIELD","CAPTURED_BY_HOST_FIELD","CONSENT_GROUP_ID_FIELD","CONSENT_GROUP_NAME_FIELD","MARKETING_CONSENT_ENFORCED_FROM_MS","Date","UTC","DEFAULT_MARKETING_CONSENT_POLICY","mode","enforceFromMs","resolveMarketingConsentPolicy","stored","value","Number","isFinite","timestampMs","getTime","toMillis","millis","call","readMarketingBasis","record","group","byHost","readConsentSource","hostId","groupId","otherGrant","capturedByHostIds","capturedByGroup","basis","assertedBy","source","basisAtMs","capturedAtMs","readConsentByHost","entry","capturedBy","readCapturedByHosts","refusedUnscoped","refusedInGroup","hostIds","find","member","consent","undefined","fields","readAttestedBasis","readOtherGrant","some","id","includes","actor","raw","Array","isArray","entries","key","Object","found","Set","array","add","legacy","marketingConsentHostIds","filter","map","sort","marketingConsentFieldsForGroup","atMs","extra","length","Error","marketingConsentAtMs","declared","name","fromEntries","marketingConsentFieldsForHost","declineMarketingConsentFields","by","kind","reason","textVersion","marketingConsentDecision","policy","verdict","marketingConsentVerdict","splitByMarketingConsent","emails","records","split","mailable","consented","consentedByOperator","grandfathered","withheld","withheldDeclined","withheldOtherHost","withheldNoBasis","email","get","decision","push"],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
* Who is allowed to see a marketplace listing (AGL-432/658/968/1196).
|
|
19
|
+
*
|
|
20
|
+
* These four predicates live in core for the same reason
|
|
21
|
+
* `MarketplaceArtifactType` (AGL-1016) and the verification policy (AGL-1217)
|
|
22
|
+
* do: a `scope:app` project may not depend on an `aglyn:addons` lib, and the
|
|
23
|
+
* console now has to ask the question outside the marketplace plugin. The
|
|
24
|
+
* listing route's `generateMetadata` (AGL-876) builds a social card from the
|
|
25
|
+
* listing document, and that card is emitted to an UNAUTHENTICATED fetcher —
|
|
26
|
+
* so it has to apply the same visibility rule the browse grid applies, or a
|
|
27
|
+
* rejected, private or taken-down listing gets its name and description
|
|
28
|
+
* unfurled into a chat message by anyone holding the URL.
|
|
29
|
+
*
|
|
30
|
+
* They were moved rather than copied. A second copy of "is this listing
|
|
31
|
+
* public" is the failure mode this file exists to prevent: the rule already
|
|
32
|
+
* gates browse AND install (AGL-965 added the second call site after the
|
|
33
|
+
* first proved insufficient on its own), and a third reading that drifts
|
|
34
|
+
* would be a disclosure bug, not a cosmetic one.
|
|
35
|
+
*
|
|
36
|
+
* `libs/plugins/marketplace/src/lib/model/marketplace.ts` re-exports all four,
|
|
37
|
+
* so publishing and install code keeps importing them from one place.
|
|
38
|
+
*/
|
|
39
|
+
import type { MarketplaceArtifactType } from './marketplace-provenance';
|
|
40
|
+
/**
|
|
41
|
+
* The listing's artifact type, tolerating the pre-AGL-654 shape.
|
|
42
|
+
*
|
|
43
|
+
* Legacy listings carry `type`/`kind` instead; a component was the absence
|
|
44
|
+
* of both, which is why this defaults there rather than throwing. Keeping
|
|
45
|
+
* the fallback means old docs keep resolving correctly instead of silently
|
|
46
|
+
* becoming un-installable.
|
|
47
|
+
*/
|
|
48
|
+
export declare function listingArtifactType(listing: {
|
|
49
|
+
artifactType?: string;
|
|
50
|
+
type?: string;
|
|
51
|
+
kind?: string;
|
|
52
|
+
}): MarketplaceArtifactType;
|
|
53
|
+
/**
|
|
54
|
+
* Private plugins (AGL-968): an org builds for its own sites without
|
|
55
|
+
* publishing to anyone. Never browsable — not even for the owner, who
|
|
56
|
+
* reaches it through their own plugins area — and installable only by the
|
|
57
|
+
* owning org (enforced in `install-plugin`, not in the UI). The review bar
|
|
58
|
+
* is identical: private code still runs on our infrastructure.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isPrivateListing(listing: {
|
|
61
|
+
visibility?: string;
|
|
62
|
+
}): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether a listing has been soft-deleted (AGL-1196).
|
|
65
|
+
*
|
|
66
|
+
* Browse used to express this as `where('deletedAt','==',null)` in the query
|
|
67
|
+
* itself. That put a MUTABLE field in a predicate: `deletedAt` flips on every
|
|
68
|
+
* unpublish/republish, and a document that stops matching a live query can
|
|
69
|
+
* leave a `noDocument` tombstone at its own path — which the detail page then
|
|
70
|
+
* reads BY ID and 404s on (AGL-827, AGL-929). Filtering in memory removes the
|
|
71
|
+
* mechanism instead of healing it.
|
|
72
|
+
*
|
|
73
|
+
* FALSY, not `=== null`. Firestore's `== null` matches only an explicit null
|
|
74
|
+
* and cannot express "field is absent", so a listing written without the field
|
|
75
|
+
* was invisible to the old query. Absent means live.
|
|
76
|
+
*
|
|
77
|
+
* Deliberately NOT folded into `isListingBrowsable`: that check carries an
|
|
78
|
+
* owner exemption, so a publisher can watch their own submission move through
|
|
79
|
+
* review. Deletion has no such exemption — a deleted listing is gone for
|
|
80
|
+
* everyone, including the org that owns it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isListingDeleted(listing: {
|
|
83
|
+
deletedAt?: unknown;
|
|
84
|
+
}): boolean;
|
|
85
|
+
/** Whether a plugin listing is publicly browsable (AGL-432). */
|
|
86
|
+
export declare function isListingBrowsable(listing: {
|
|
87
|
+
artifactType?: string;
|
|
88
|
+
type?: string;
|
|
89
|
+
kind?: string;
|
|
90
|
+
reviewStatus?: string;
|
|
91
|
+
hiddenAt?: unknown;
|
|
92
|
+
visibility?: string;
|
|
93
|
+
}): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Listing fields a publisher may legitimately write, each with the reason
|
|
96
|
+
* (AGL-1361). Everything else must be denied in the rules' listing key diff.
|
|
97
|
+
*
|
|
98
|
+
* They live here rather than beside `MarketplaceListing` for the same reason
|
|
99
|
+
* the visibility policy does: this module is what READS the fields that decide
|
|
100
|
+
* whether a listing is browsable, and `scope:app` may not depend on
|
|
101
|
+
* `aglyn:addons`. `marketplace.ts` re-exports them so publishing code keeps
|
|
102
|
+
* one import site.
|
|
103
|
+
*
|
|
104
|
+
* The list is short on purpose. The only client-side write to a listing
|
|
105
|
+
* document anywhere in the repo is the unpublish/republish toggle in the org
|
|
106
|
+
* seller panel, which writes `deletedAt` alone — publish, content edits,
|
|
107
|
+
* review and install all go through Admin-SDK routes. So the fields below are
|
|
108
|
+
* classified writable because a publisher OWNS them, not because anything
|
|
109
|
+
* would break: denying the rest cost nothing, which is how AGL-1364 closed.
|
|
110
|
+
*/
|
|
111
|
+
export declare const LISTING_CLIENT_WRITABLE_FIELDS: Readonly<Record<string, string>>;
|
|
112
|
+
/**
|
|
113
|
+
* Listing keys that never reach Firestore, so the rules have no opinion.
|
|
114
|
+
*
|
|
115
|
+
* Empty today — `MarketplaceListing` declares no `$id`, because listing code
|
|
116
|
+
* carries the id alongside the data rather than folding it in. Kept as the
|
|
117
|
+
* partition's third arm so a future projection field has a home that is not
|
|
118
|
+
* "client-writable".
|
|
119
|
+
*/
|
|
120
|
+
export declare const LISTING_UNPERSISTED_FIELDS: Readonly<Record<string, 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
|
+
* Who is allowed to see a marketplace listing (AGL-432/658/968/1196).
|
|
18
|
+
*
|
|
19
|
+
* These four predicates live in core for the same reason
|
|
20
|
+
* `MarketplaceArtifactType` (AGL-1016) and the verification policy (AGL-1217)
|
|
21
|
+
* do: a `scope:app` project may not depend on an `aglyn:addons` lib, and the
|
|
22
|
+
* console now has to ask the question outside the marketplace plugin. The
|
|
23
|
+
* listing route's `generateMetadata` (AGL-876) builds a social card from the
|
|
24
|
+
* listing document, and that card is emitted to an UNAUTHENTICATED fetcher —
|
|
25
|
+
* so it has to apply the same visibility rule the browse grid applies, or a
|
|
26
|
+
* rejected, private or taken-down listing gets its name and description
|
|
27
|
+
* unfurled into a chat message by anyone holding the URL.
|
|
28
|
+
*
|
|
29
|
+
* They were moved rather than copied. A second copy of "is this listing
|
|
30
|
+
* public" is the failure mode this file exists to prevent: the rule already
|
|
31
|
+
* gates browse AND install (AGL-965 added the second call site after the
|
|
32
|
+
* first proved insufficient on its own), and a third reading that drifts
|
|
33
|
+
* would be a disclosure bug, not a cosmetic one.
|
|
34
|
+
*
|
|
35
|
+
* `libs/plugins/marketplace/src/lib/model/marketplace.ts` re-exports all four,
|
|
36
|
+
* so publishing and install code keeps importing them from one place.
|
|
37
|
+
*/ /**
|
|
38
|
+
* The listing's artifact type, tolerating the pre-AGL-654 shape.
|
|
39
|
+
*
|
|
40
|
+
* Legacy listings carry `type`/`kind` instead; a component was the absence
|
|
41
|
+
* of both, which is why this defaults there rather than throwing. Keeping
|
|
42
|
+
* the fallback means old docs keep resolving correctly instead of silently
|
|
43
|
+
* becoming un-installable.
|
|
44
|
+
*/ export function listingArtifactType(listing) {
|
|
45
|
+
if (listing.artifactType) return listing.artifactType;
|
|
46
|
+
if (listing.kind === 'template') return 'template';
|
|
47
|
+
if (listing.type === 'plugin') return 'plugin';
|
|
48
|
+
return 'component';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Private plugins (AGL-968): an org builds for its own sites without
|
|
52
|
+
* publishing to anyone. Never browsable — not even for the owner, who
|
|
53
|
+
* reaches it through their own plugins area — and installable only by the
|
|
54
|
+
* owning org (enforced in `install-plugin`, not in the UI). The review bar
|
|
55
|
+
* is identical: private code still runs on our infrastructure.
|
|
56
|
+
*/ export function isPrivateListing(listing) {
|
|
57
|
+
return listing.visibility === 'private';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a listing has been soft-deleted (AGL-1196).
|
|
61
|
+
*
|
|
62
|
+
* Browse used to express this as `where('deletedAt','==',null)` in the query
|
|
63
|
+
* itself. That put a MUTABLE field in a predicate: `deletedAt` flips on every
|
|
64
|
+
* unpublish/republish, and a document that stops matching a live query can
|
|
65
|
+
* leave a `noDocument` tombstone at its own path — which the detail page then
|
|
66
|
+
* reads BY ID and 404s on (AGL-827, AGL-929). Filtering in memory removes the
|
|
67
|
+
* mechanism instead of healing it.
|
|
68
|
+
*
|
|
69
|
+
* FALSY, not `=== null`. Firestore's `== null` matches only an explicit null
|
|
70
|
+
* and cannot express "field is absent", so a listing written without the field
|
|
71
|
+
* was invisible to the old query. Absent means live.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately NOT folded into `isListingBrowsable`: that check carries an
|
|
74
|
+
* owner exemption, so a publisher can watch their own submission move through
|
|
75
|
+
* review. Deletion has no such exemption — a deleted listing is gone for
|
|
76
|
+
* everyone, including the org that owns it.
|
|
77
|
+
*/ export function isListingDeleted(listing) {
|
|
78
|
+
return Boolean(listing.deletedAt);
|
|
79
|
+
}
|
|
80
|
+
/** Whether a plugin listing is publicly browsable (AGL-432). */ export function isListingBrowsable(listing) {
|
|
81
|
+
// Staff takedown applies to EVERY artifact type (AGL-658). Pre-publication
|
|
82
|
+
// review is plugin-only — plugins execute code, so they earn the wait —
|
|
83
|
+
// but a component or template that turns out to be abusive must be
|
|
84
|
+
// removable too, and before this it simply was not: the early return
|
|
85
|
+
// below meant anything non-plugin was permanently browsable.
|
|
86
|
+
if (listing.hiddenAt) return false;
|
|
87
|
+
// Private listings never reach the marketplace (AGL-968).
|
|
88
|
+
if (isPrivateListing(listing)) return false;
|
|
89
|
+
if (listingArtifactType(listing) !== 'plugin') return true;
|
|
90
|
+
return listing.reviewStatus === undefined || listing.reviewStatus === 'listed' || listing.reviewStatus === 'verified';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Listing fields a publisher may legitimately write, each with the reason
|
|
94
|
+
* (AGL-1361). Everything else must be denied in the rules' listing key diff.
|
|
95
|
+
*
|
|
96
|
+
* They live here rather than beside `MarketplaceListing` for the same reason
|
|
97
|
+
* the visibility policy does: this module is what READS the fields that decide
|
|
98
|
+
* whether a listing is browsable, and `scope:app` may not depend on
|
|
99
|
+
* `aglyn:addons`. `marketplace.ts` re-exports them so publishing code keeps
|
|
100
|
+
* one import site.
|
|
101
|
+
*
|
|
102
|
+
* The list is short on purpose. The only client-side write to a listing
|
|
103
|
+
* document anywhere in the repo is the unpublish/republish toggle in the org
|
|
104
|
+
* seller panel, which writes `deletedAt` alone — publish, content edits,
|
|
105
|
+
* review and install all go through Admin-SDK routes. So the fields below are
|
|
106
|
+
* classified writable because a publisher OWNS them, not because anything
|
|
107
|
+
* would break: denying the rest cost nothing, which is how AGL-1364 closed.
|
|
108
|
+
*/ export const LISTING_CLIENT_WRITABLE_FIELDS = {
|
|
109
|
+
displayName: 'The listing title. Publisher-authored storefront copy, validated by ' + '`validateListingContent` on the API path and sanitized at render — ' + 'renderers never trust this document.',
|
|
110
|
+
description: 'Publisher-authored storefront copy. Same handling as `displayName`.',
|
|
111
|
+
category: 'The single legacy category. Publisher-chosen shelf placement; it ' + 'orders a browse page and gates nothing.',
|
|
112
|
+
categories: 'The AGL-430 multi-category list. Same reasoning as `category` — ' + 'placement, not permission.',
|
|
113
|
+
readme: 'Markdown documentation for the listing page (no raw HTML). ' + 'Publisher-authored and sanitized at render.',
|
|
114
|
+
// `logoUrl` and `screenshots` were here, justified as "sanitized at
|
|
115
|
+
// render" (AGL-1701). They were not: listing-content.component.tsx hands
|
|
116
|
+
// both straight to an `<img src>`, and `logoUrl` additionally becomes an
|
|
117
|
+
// `og:image` served to unauthenticated unfurlers. Publisher-authored
|
|
118
|
+
// ARTWORK is not like publisher-authored COPY — copy is read by the
|
|
119
|
+
// viewer, artwork is FETCHED by the viewer's browser from whatever host
|
|
120
|
+
// the string names, disclosing their IP and a `Referer` naming the
|
|
121
|
+
// console and their org to a recipient on no subprocessor register.
|
|
122
|
+
// Denied in the rules and held to first-party media on the API path.
|
|
123
|
+
homepageUrl: 'Publisher-authored outbound link, sanitized at render. It points ' + 'away from the platform and decides nothing here.',
|
|
124
|
+
repositoryUrl: 'Publisher-authored outbound link. Same as `homepageUrl`.',
|
|
125
|
+
license: 'An SPDX-ish label the publisher asserts about their own code. A claim ' + 'shown to buyers, not a permission the platform grants.',
|
|
126
|
+
deletedAt: 'The unpublish/republish toggle — the ONE field the console writes ' + 'client-side, and the publisher\'s own decision to withdraw their ' + 'listing. Staff takedown is `hiddenAt`, which is denied precisely so ' + 'an owner cannot un-hide themselves.',
|
|
127
|
+
visibility: 'Private vs public (AGL-968). Genuinely the publisher\'s choice, and ' + 'it only ever NARROWS reach: a private plugin faces the identical ' + 'review bar, and the review gate a publisher might try to escape by ' + 'relabelling is closed by denying `artifactType`/`type`/`kind`.',
|
|
128
|
+
pluginId: 'The manifest id a plugin listing declares (AGL-45). Rewriting it ' + 'breaks the publisher\'s own install resolution; the artifact that ' + 'actually runs is pinned by sha256 in the staff-only `pluginVersions`.'
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Listing keys that never reach Firestore, so the rules have no opinion.
|
|
132
|
+
*
|
|
133
|
+
* Empty today — `MarketplaceListing` declares no `$id`, because listing code
|
|
134
|
+
* carries the id alongside the data rather than folding it in. Kept as the
|
|
135
|
+
* partition's third arm so a future projection field has a home that is not
|
|
136
|
+
* "client-writable".
|
|
137
|
+
*/ export const LISTING_UNPERSISTED_FIELDS = {};
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=marketplace-listing-visibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/marketplace-listing-visibility.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 * Who is allowed to see a marketplace listing (AGL-432/658/968/1196).\n *\n * These four predicates live in core for the same reason\n * `MarketplaceArtifactType` (AGL-1016) and the verification policy (AGL-1217)\n * do: a `scope:app` project may not depend on an `aglyn:addons` lib, and the\n * console now has to ask the question outside the marketplace plugin. The\n * listing route's `generateMetadata` (AGL-876) builds a social card from the\n * listing document, and that card is emitted to an UNAUTHENTICATED fetcher —\n * so it has to apply the same visibility rule the browse grid applies, or a\n * rejected, private or taken-down listing gets its name and description\n * unfurled into a chat message by anyone holding the URL.\n *\n * They were moved rather than copied. A second copy of \"is this listing\n * public\" is the failure mode this file exists to prevent: the rule already\n * gates browse AND install (AGL-965 added the second call site after the\n * first proved insufficient on its own), and a third reading that drifts\n * would be a disclosure bug, not a cosmetic one.\n *\n * `libs/plugins/marketplace/src/lib/model/marketplace.ts` re-exports all four,\n * so publishing and install code keeps importing them from one place.\n */\n\nimport type { MarketplaceArtifactType } from './marketplace-provenance'\n\n/**\n * The listing's artifact type, tolerating the pre-AGL-654 shape.\n *\n * Legacy listings carry `type`/`kind` instead; a component was the absence\n * of both, which is why this defaults there rather than throwing. Keeping\n * the fallback means old docs keep resolving correctly instead of silently\n * becoming un-installable.\n */\nexport function listingArtifactType(listing: {\n artifactType?: string\n type?: string\n kind?: string\n}): MarketplaceArtifactType {\n if (listing.artifactType) return listing.artifactType as MarketplaceArtifactType\n if (listing.kind === 'template') return 'template'\n if (listing.type === 'plugin') return 'plugin'\n return 'component'\n}\n\n/**\n * Private plugins (AGL-968): an org builds for its own sites without\n * publishing to anyone. Never browsable — not even for the owner, who\n * reaches it through their own plugins area — and installable only by the\n * owning org (enforced in `install-plugin`, not in the UI). The review bar\n * is identical: private code still runs on our infrastructure.\n */\nexport function isPrivateListing(listing: { visibility?: string }): boolean {\n return listing.visibility === 'private'\n}\n\n/**\n * Whether a listing has been soft-deleted (AGL-1196).\n *\n * Browse used to express this as `where('deletedAt','==',null)` in the query\n * itself. That put a MUTABLE field in a predicate: `deletedAt` flips on every\n * unpublish/republish, and a document that stops matching a live query can\n * leave a `noDocument` tombstone at its own path — which the detail page then\n * reads BY ID and 404s on (AGL-827, AGL-929). Filtering in memory removes the\n * mechanism instead of healing it.\n *\n * FALSY, not `=== null`. Firestore's `== null` matches only an explicit null\n * and cannot express \"field is absent\", so a listing written without the field\n * was invisible to the old query. Absent means live.\n *\n * Deliberately NOT folded into `isListingBrowsable`: that check carries an\n * owner exemption, so a publisher can watch their own submission move through\n * review. Deletion has no such exemption — a deleted listing is gone for\n * everyone, including the org that owns it.\n */\nexport function isListingDeleted(listing: { deletedAt?: unknown }): boolean {\n return Boolean(listing.deletedAt)\n}\n\n/** Whether a plugin listing is publicly browsable (AGL-432). */\nexport function isListingBrowsable(listing: {\n artifactType?: string\n type?: string\n kind?: string\n reviewStatus?: string\n hiddenAt?: unknown\n visibility?: string\n}): boolean {\n // Staff takedown applies to EVERY artifact type (AGL-658). Pre-publication\n // review is plugin-only — plugins execute code, so they earn the wait —\n // but a component or template that turns out to be abusive must be\n // removable too, and before this it simply was not: the early return\n // below meant anything non-plugin was permanently browsable.\n if (listing.hiddenAt) return false\n // Private listings never reach the marketplace (AGL-968).\n if (isPrivateListing(listing)) return false\n if (listingArtifactType(listing) !== 'plugin') return true\n return (\n listing.reviewStatus === undefined ||\n listing.reviewStatus === 'listed' ||\n listing.reviewStatus === 'verified'\n )\n}\n\n/**\n * Listing fields a publisher may legitimately write, each with the reason\n * (AGL-1361). Everything else must be denied in the rules' listing key diff.\n *\n * They live here rather than beside `MarketplaceListing` for the same reason\n * the visibility policy does: this module is what READS the fields that decide\n * whether a listing is browsable, and `scope:app` may not depend on\n * `aglyn:addons`. `marketplace.ts` re-exports them so publishing code keeps\n * one import site.\n *\n * The list is short on purpose. The only client-side write to a listing\n * document anywhere in the repo is the unpublish/republish toggle in the org\n * seller panel, which writes `deletedAt` alone — publish, content edits,\n * review and install all go through Admin-SDK routes. So the fields below are\n * classified writable because a publisher OWNS them, not because anything\n * would break: denying the rest cost nothing, which is how AGL-1364 closed.\n */\nexport const LISTING_CLIENT_WRITABLE_FIELDS: Readonly<Record<string, string>> =\n {\n displayName:\n 'The listing title. Publisher-authored storefront copy, validated by ' +\n '`validateListingContent` on the API path and sanitized at render — ' +\n 'renderers never trust this document.',\n description: 'Publisher-authored storefront copy. Same handling as `displayName`.',\n category:\n 'The single legacy category. Publisher-chosen shelf placement; it ' +\n 'orders a browse page and gates nothing.',\n categories:\n 'The AGL-430 multi-category list. Same reasoning as `category` — ' +\n 'placement, not permission.',\n readme:\n 'Markdown documentation for the listing page (no raw HTML). ' +\n 'Publisher-authored and sanitized at render.',\n // `logoUrl` and `screenshots` were here, justified as \"sanitized at\n // render\" (AGL-1701). They were not: listing-content.component.tsx hands\n // both straight to an `<img src>`, and `logoUrl` additionally becomes an\n // `og:image` served to unauthenticated unfurlers. Publisher-authored\n // ARTWORK is not like publisher-authored COPY — copy is read by the\n // viewer, artwork is FETCHED by the viewer's browser from whatever host\n // the string names, disclosing their IP and a `Referer` naming the\n // console and their org to a recipient on no subprocessor register.\n // Denied in the rules and held to first-party media on the API path.\n homepageUrl:\n 'Publisher-authored outbound link, sanitized at render. It points ' +\n 'away from the platform and decides nothing here.',\n repositoryUrl: 'Publisher-authored outbound link. Same as `homepageUrl`.',\n license:\n 'An SPDX-ish label the publisher asserts about their own code. A claim ' +\n 'shown to buyers, not a permission the platform grants.',\n deletedAt:\n 'The unpublish/republish toggle — the ONE field the console writes ' +\n 'client-side, and the publisher\\'s own decision to withdraw their ' +\n 'listing. Staff takedown is `hiddenAt`, which is denied precisely so ' +\n 'an owner cannot un-hide themselves.',\n visibility:\n 'Private vs public (AGL-968). Genuinely the publisher\\'s choice, and ' +\n 'it only ever NARROWS reach: a private plugin faces the identical ' +\n 'review bar, and the review gate a publisher might try to escape by ' +\n 'relabelling is closed by denying `artifactType`/`type`/`kind`.',\n pluginId:\n 'The manifest id a plugin listing declares (AGL-45). Rewriting it ' +\n 'breaks the publisher\\'s own install resolution; the artifact that ' +\n 'actually runs is pinned by sha256 in the staff-only `pluginVersions`.',\n }\n\n/**\n * Listing keys that never reach Firestore, so the rules have no opinion.\n *\n * Empty today — `MarketplaceListing` declares no `$id`, because listing code\n * carries the id alongside the data rather than folding it in. Kept as the\n * partition's third arm so a future projection field has a home that is not\n * \"client-writable\".\n */\nexport const LISTING_UNPERSISTED_FIELDS: Readonly<Record<string, string>> = {}\n"],"names":["listingArtifactType","listing","artifactType","kind","type","isPrivateListing","visibility","isListingDeleted","Boolean","deletedAt","isListingBrowsable","hiddenAt","reviewStatus","undefined","LISTING_CLIENT_WRITABLE_FIELDS","displayName","description","category","categories","readme","homepageUrl","repositoryUrl","license","pluginId","LISTING_UNPERSISTED_FIELDS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;CAqBC,GAID;;;;;;;CAOC,GACD,OAAO,SAASA,oBAAoBC,OAInC;IACC,IAAIA,QAAQC,YAAY,EAAE,OAAOD,QAAQC,YAAY;IACrD,IAAID,QAAQE,IAAI,KAAK,YAAY,OAAO;IACxC,IAAIF,QAAQG,IAAI,KAAK,UAAU,OAAO;IACtC,OAAO;AACT;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,iBAAiBJ,OAAgC;IAC/D,OAAOA,QAAQK,UAAU,KAAK;AAChC;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,iBAAiBN,OAAgC;IAC/D,OAAOO,QAAQP,QAAQQ,SAAS;AAClC;AAEA,8DAA8D,GAC9D,OAAO,SAASC,mBAAmBT,OAOlC;IACC,2EAA2E;IAC3E,wEAAwE;IACxE,mEAAmE;IACnE,qEAAqE;IACrE,6DAA6D;IAC7D,IAAIA,QAAQU,QAAQ,EAAE,OAAO;IAC7B,0DAA0D;IAC1D,IAAIN,iBAAiBJ,UAAU,OAAO;IACtC,IAAID,oBAAoBC,aAAa,UAAU,OAAO;IACtD,OACEA,QAAQW,YAAY,KAAKC,aACzBZ,QAAQW,YAAY,KAAK,YACzBX,QAAQW,YAAY,KAAK;AAE7B;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,MAAME,iCACX;IACEC,aACE,yEACA,wEACA;IACFC,aAAa;IACbC,UACE,sEACA;IACFC,YACE,qEACA;IACFC,QACE,gEACA;IACF,oEAAoE;IACpE,yEAAyE;IACzE,yEAAyE;IACzE,qEAAqE;IACrE,oEAAoE;IACpE,wEAAwE;IACxE,mEAAmE;IACnE,oEAAoE;IACpE,qEAAqE;IACrEC,aACE,sEACA;IACFC,eAAe;IACfC,SACE,2EACA;IACFb,WACE,uEACA,sEACA,yEACA;IACFH,YACE,yEACA,sEACA,wEACA;IACFiB,UACE,sEACA,uEACA;AACJ,EAAC;AAEH;;;;;;;CAOC,GACD,OAAO,MAAMC,6BAA+D,CAAC,EAAC"}
|