@aglyn/aglyn 1.0.0-beta.218 → 1.0.0-beta.219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -11
- package/src/lib/app-utils/actions.d.ts +25 -374
- package/src/lib/app-utils/actions.js +101 -561
- package/src/lib/app-utils/actions.js.map +1 -1
- package/src/lib/app-utils/activity-presenter.d.ts +3 -3
- package/src/lib/app-utils/activity-presenter.js +36 -52
- package/src/lib/app-utils/activity-presenter.js.map +1 -1
- package/src/lib/app-utils/analytics-events.d.ts +1 -1
- package/src/lib/app-utils/analytics-events.js.map +1 -1
- package/src/lib/app-utils/analytics-provider.d.ts +182 -0
- package/src/lib/app-utils/analytics-provider.js +133 -0
- package/src/lib/app-utils/analytics-provider.js.map +1 -0
- package/src/lib/app-utils/api-plugins.d.ts +46 -0
- package/src/lib/app-utils/api-plugins.js +19 -0
- package/src/lib/app-utils/api-plugins.js.map +1 -1
- package/src/lib/app-utils/{marketplace-overrides.d.ts → artifact-overrides.d.ts} +9 -6
- package/src/lib/app-utils/{marketplace-overrides.js → artifact-overrides.js} +10 -7
- package/src/lib/app-utils/artifact-overrides.js.map +1 -0
- package/src/lib/app-utils/{marketplace-provenance.d.ts → artifact-provenance.d.ts} +12 -10
- package/src/lib/app-utils/{marketplace-provenance.js → artifact-provenance.js} +9 -7
- package/src/lib/app-utils/artifact-provenance.js.map +1 -0
- package/src/lib/app-utils/assist-credits.js +2 -1
- package/src/lib/app-utils/assist-credits.js.map +1 -1
- package/src/lib/app-utils/binding-token-catalog.d.ts +5 -13
- package/src/lib/app-utils/binding-token-catalog.js +17 -41
- package/src/lib/app-utils/binding-token-catalog.js.map +1 -1
- package/src/lib/app-utils/brand-support-line.d.ts +36 -0
- package/src/lib/app-utils/brand-support-line.js +38 -0
- package/src/lib/app-utils/brand-support-line.js.map +1 -0
- package/src/lib/app-utils/campaign-touch.d.ts +5 -5
- package/src/lib/app-utils/campaign-touch.js +8 -8
- package/src/lib/app-utils/campaign-touch.js.map +1 -1
- package/src/lib/app-utils/collection-entries.d.ts +2 -2
- package/src/lib/app-utils/collection-entries.js.map +1 -1
- package/src/lib/app-utils/consent-group-change.d.ts +4 -0
- package/src/lib/app-utils/consent-group-change.js.map +1 -1
- package/src/lib/app-utils/console-record-links.d.ts +10 -89
- package/src/lib/app-utils/console-record-links.js +3 -107
- package/src/lib/app-utils/console-record-links.js.map +1 -1
- package/src/lib/app-utils/console-routes.d.ts +16 -7
- package/src/lib/app-utils/console-routes.js +9 -3
- package/src/lib/app-utils/console-routes.js.map +1 -1
- package/src/lib/app-utils/contacts.d.ts +23 -1
- package/src/lib/app-utils/contacts.js +21 -5
- package/src/lib/app-utils/contacts.js.map +1 -1
- package/src/lib/app-utils/cross-pool-desync.d.ts +39 -0
- package/src/lib/app-utils/cross-pool-desync.js +47 -0
- package/src/lib/app-utils/cross-pool-desync.js.map +1 -0
- package/src/lib/app-utils/csv-upload.d.ts +29 -0
- package/src/lib/app-utils/csv-upload.js +29 -0
- package/src/lib/app-utils/csv-upload.js.map +1 -0
- package/src/lib/app-utils/dataset-models.d.ts +5 -5
- package/src/lib/app-utils/dataset-models.js +19 -2
- package/src/lib/app-utils/dataset-models.js.map +1 -1
- package/src/lib/app-utils/datasets.d.ts +65 -11
- package/src/lib/app-utils/datasets.js +74 -11
- package/src/lib/app-utils/datasets.js.map +1 -1
- package/src/lib/app-utils/docs-deflection.d.ts +6 -6
- package/src/lib/app-utils/docs-deflection.js.map +1 -1
- package/src/lib/app-utils/docs-help.generated.d.ts +12 -6
- package/src/lib/app-utils/docs-help.generated.js +21 -6
- package/src/lib/app-utils/docs-help.generated.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.d.ts +2 -2
- package/src/lib/app-utils/docs-index.generated.js +514 -318
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/docs-retrieval.d.ts +3 -3
- package/src/lib/app-utils/docs-retrieval.js +14 -10
- package/src/lib/app-utils/docs-retrieval.js.map +1 -1
- package/src/lib/app-utils/duplicate-resource.d.ts +4 -1
- package/src/lib/app-utils/duplicate-resource.js +7 -4
- package/src/lib/app-utils/duplicate-resource.js.map +1 -1
- package/src/lib/app-utils/dynamic-list-rule.d.ts +1 -1
- package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
- package/src/lib/app-utils/element-ui.d.ts +1 -1
- package/src/lib/app-utils/element-ui.js +1 -1
- package/src/lib/app-utils/element-ui.js.map +1 -1
- package/src/lib/app-utils/email-starter-policy.js +3 -3
- package/src/lib/app-utils/email-starter-policy.js.map +1 -1
- package/src/lib/app-utils/entity-picker-context.d.ts +10 -5
- package/src/lib/app-utils/entity-picker-context.js.map +1 -1
- package/src/lib/app-utils/error-beacon.d.ts +75 -6
- package/src/lib/app-utils/error-beacon.js +160 -21
- package/src/lib/app-utils/error-beacon.js.map +1 -1
- package/src/lib/app-utils/expand-repeatables.d.ts +10 -10
- package/src/lib/app-utils/expand-repeatables.js +10 -10
- package/src/lib/app-utils/expand-repeatables.js.map +1 -1
- package/src/lib/app-utils/host-events.d.ts +82 -0
- package/src/lib/app-utils/host-events.js +62 -0
- package/src/lib/app-utils/host-events.js.map +1 -0
- package/src/lib/app-utils/margin-utilization.d.ts +4 -69
- package/src/lib/app-utils/margin-utilization.js +82 -69
- package/src/lib/app-utils/margin-utilization.js.map +1 -1
- package/src/lib/app-utils/node-definition-sanitizer.d.ts +16 -16
- package/src/lib/app-utils/node-definition-sanitizer.js +24 -20
- package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -1
- package/src/lib/app-utils/node-interactions.d.ts +7 -7
- package/src/lib/app-utils/node-interactions.js.map +1 -1
- package/src/lib/app-utils/notifications.d.ts +78 -47
- package/src/lib/app-utils/notifications.js +63 -44
- package/src/lib/app-utils/notifications.js.map +1 -1
- package/src/lib/app-utils/payments-configured.d.ts +8 -21
- package/src/lib/app-utils/payments-configured.js +8 -23
- package/src/lib/app-utils/payments-configured.js.map +1 -1
- package/src/lib/app-utils/plan-entitlements.d.ts +43 -128
- package/src/lib/app-utils/plan-entitlements.js +145 -304
- package/src/lib/app-utils/plan-entitlements.js.map +1 -1
- package/src/lib/app-utils/platform-brand.d.ts +10 -0
- package/src/lib/app-utils/platform-brand.js +14 -3
- package/src/lib/app-utils/platform-brand.js.map +1 -1
- package/src/lib/app-utils/plugin-api-rate-limit.d.ts +7 -1
- package/src/lib/app-utils/plugin-api-rate-limit.js +11 -3
- package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -1
- package/src/lib/app-utils/plugin-bundle-checks.js +2 -2
- package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
- package/src/lib/app-utils/plugin-host-events.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-host-events.generated.js +169 -0
- package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -0
- package/src/lib/app-utils/plugin-release-flags.generated.d.ts +12 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js +106 -0
- package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -0
- package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
- package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
- package/src/lib/app-utils/release-flags.d.ts +14 -5
- package/src/lib/app-utils/release-flags.js +21 -125
- package/src/lib/app-utils/release-flags.js.map +1 -1
- package/src/lib/app-utils/repeat-sources.d.ts +9 -0
- package/src/lib/app-utils/repeat-sources.js.map +1 -1
- package/src/lib/app-utils/retained-refusals.d.ts +73 -0
- package/src/lib/app-utils/retained-refusals.js +186 -0
- package/src/lib/app-utils/retained-refusals.js.map +1 -0
- package/src/lib/app-utils/scope-tokens.d.ts +27 -0
- package/src/lib/app-utils/scope-tokens.js +61 -0
- package/src/lib/app-utils/scope-tokens.js.map +1 -1
- package/src/lib/app-utils/screen-link-context.js +3 -3
- package/src/lib/app-utils/screen-link-context.js.map +1 -1
- package/src/lib/app-utils/screen-route.js +1 -1
- package/src/lib/app-utils/screen-route.js.map +1 -1
- package/src/lib/app-utils/seo-audit.d.ts +132 -0
- package/src/lib/app-utils/seo-audit.js +246 -0
- package/src/lib/app-utils/seo-audit.js.map +1 -0
- package/src/lib/app-utils/seo-keywords.d.ts +48 -0
- package/src/lib/app-utils/seo-keywords.js +60 -0
- package/src/lib/app-utils/seo-keywords.js.map +1 -0
- package/src/lib/app-utils/seo-page-facts.d.ts +66 -0
- package/src/lib/app-utils/seo-page-facts.js +155 -0
- package/src/lib/app-utils/seo-page-facts.js.map +1 -0
- package/src/lib/app-utils/seo-site-scan.d.ts +126 -0
- package/src/lib/app-utils/seo-site-scan.js +162 -0
- package/src/lib/app-utils/seo-site-scan.js.map +1 -0
- package/src/lib/app-utils/seo-title-variables.js +1 -1
- package/src/lib/app-utils/seo-title-variables.js.map +1 -1
- package/src/lib/app-utils/server.d.ts +6 -9
- package/src/lib/app-utils/server.js +17 -27
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/app-utils/site-interactions.d.ts +501 -0
- package/src/lib/app-utils/site-interactions.js +554 -0
- package/src/lib/app-utils/site-interactions.js.map +1 -0
- package/src/lib/app-utils/{marketplace-theme.d.ts → site-theme.d.ts} +13 -2
- package/src/lib/app-utils/{marketplace-theme.js → site-theme.js} +2 -2
- package/src/lib/app-utils/site-theme.js.map +1 -0
- package/src/lib/app-utils/sitemap.d.ts +4 -3
- package/src/lib/app-utils/sitemap.js +9 -7
- package/src/lib/app-utils/sitemap.js.map +1 -1
- package/src/lib/app-utils/stale-build-error.d.ts +51 -0
- package/src/lib/app-utils/stale-build-error.js +100 -1
- package/src/lib/app-utils/stale-build-error.js.map +1 -1
- package/src/lib/app-utils/tax-jurisdiction-figures.d.ts +118 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js +123 -0
- package/src/lib/app-utils/tax-jurisdiction-figures.js.map +1 -0
- package/src/lib/app-utils/theme-library.d.ts +10 -3
- package/src/lib/app-utils/theme-library.js +13 -4
- package/src/lib/app-utils/theme-library.js.map +1 -1
- package/src/lib/app-utils/{campaign-attribution.d.ts → utm-attribution.d.ts} +11 -6
- package/src/lib/app-utils/{campaign-attribution.js → utm-attribution.js} +13 -8
- package/src/lib/app-utils/utm-attribution.js.map +1 -0
- package/src/lib/app-utils/{campaign-forwarding.d.ts → utm-forwarding.d.ts} +14 -14
- package/src/lib/app-utils/{campaign-forwarding.js → utm-forwarding.js} +26 -26
- package/src/lib/app-utils/utm-forwarding.js.map +1 -0
- package/src/lib/app-utils/video-object.js +6 -5
- package/src/lib/app-utils/video-object.js.map +1 -1
- package/src/lib/app-utils/visitor-consent.d.ts +41 -164
- package/src/lib/app-utils/visitor-consent.js +66 -277
- package/src/lib/app-utils/visitor-consent.js.map +1 -1
- package/src/lib/app-utils/web-vitals-rum.d.ts +7 -7
- package/src/lib/app-utils/web-vitals-rum.js +31 -63
- package/src/lib/app-utils/web-vitals-rum.js.map +1 -1
- package/src/lib/app-utils/where-used.d.ts +11 -6
- package/src/lib/app-utils/where-used.js +3 -8
- package/src/lib/app-utils/where-used.js.map +1 -1
- package/src/lib/foundation/definitions/components.types.d.ts +1 -1
- package/src/lib/foundation/definitions/components.types.js +1 -1
- package/src/lib/foundation/definitions/components.types.js.map +1 -1
- package/src/lib/foundation/definitions/org-billing.types.d.ts +9 -135
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/foundation/definitions/organization.types.d.ts +1 -3
- package/src/lib/foundation/definitions/organization.types.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +11 -5
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +16 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js +20 -0
- package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
- package/src/lib/foundation/index.d.ts +1 -1
- package/src/lib/foundation/index.js.map +1 -1
- package/src/lib/plugin-manager/besigner-documents.d.ts +96 -0
- package/src/lib/plugin-manager/besigner-documents.js +61 -0
- package/src/lib/plugin-manager/besigner-documents.js.map +1 -0
- package/src/lib/plugin-manager/computed-variables.d.ts +71 -0
- package/src/lib/plugin-manager/computed-variables.js +74 -0
- package/src/lib/plugin-manager/computed-variables.js.map +1 -0
- package/src/lib/plugin-manager/custom-fields.d.ts +29 -2
- package/src/lib/plugin-manager/custom-fields.js +47 -1
- package/src/lib/plugin-manager/custom-fields.js.map +1 -1
- package/src/lib/plugin-manager/feature-plugins.d.ts +292 -20
- package/src/lib/plugin-manager/feature-plugins.js +105 -17
- package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
- package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +123 -0
- package/src/lib/plugin-manager/first-party-plugins.generated.js +784 -10
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
- package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
- package/src/lib/plugin-manager/plugin-dependents.js +75 -0
- package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
- package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
- package/src/lib/plugin-manager/plugin-distribution.js +45 -0
- package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
- package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
- package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
- package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
- package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
- package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
- package/src/lib/plugin-manager/plugin-record-index.js +51 -0
- package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
- package/src/lib/plugin-manager/plugin-record-written.js +42 -0
- package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
- package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
- package/src/lib/plugin-manager/plugin-site-export.js +40 -0
- package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
- package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
- package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
- package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
- package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
- package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-axes.d.ts +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
- package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
- package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
- package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
- package/src/lib/plugin-manager/record-captured-contact.js +10 -32
- package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
- package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
- package/src/lib/plugin-manager/repeat-rows.js +108 -0
- package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
- package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
- package/src/lib/plugin-manager/submission-record-target.js +125 -0
- package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
- package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
- package/src/lib/plugin-manager/video-embed-provider.js +165 -0
- package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
- package/src/lib/types/nodes.d.ts +2 -2
- package/src/lib/types/nodes.js.map +1 -1
- package/src/lib/app-utils/campaign-attribution.js.map +0 -1
- package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
- package/src/lib/app-utils/contact-holder.d.ts +0 -68
- package/src/lib/app-utils/contact-holder.js +0 -129
- package/src/lib/app-utils/contact-holder.js.map +0 -1
- package/src/lib/app-utils/contact-merge.d.ts +0 -136
- package/src/lib/app-utils/contact-merge.js +0 -412
- package/src/lib/app-utils/contact-merge.js.map +0 -1
- package/src/lib/app-utils/crm-csv.d.ts +0 -239
- package/src/lib/app-utils/crm-csv.js +0 -467
- package/src/lib/app-utils/crm-csv.js.map +0 -1
- package/src/lib/app-utils/crm-digest.d.ts +0 -185
- package/src/lib/app-utils/crm-digest.js +0 -272
- package/src/lib/app-utils/crm-digest.js.map +0 -1
- package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
- package/src/lib/app-utils/form-dataset-binding.js +0 -74
- package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
- package/src/lib/app-utils/list-import.d.ts +0 -158
- package/src/lib/app-utils/list-import.js +0 -332
- package/src/lib/app-utils/list-import.js.map +0 -1
- package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
- package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
- package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
- package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
- package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
- package/src/lib/app-utils/marketplace-theme.js.map +0 -1
- package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
- package/src/lib/app-utils/marketplace-update-state.js +0 -132
- package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
- package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
- package/src/lib/app-utils/publisher-agreement.js +0 -321
- package/src/lib/app-utils/publisher-agreement.js.map +0 -1
- package/src/lib/app-utils/wistia-embed.d.ts +0 -104
- package/src/lib/app-utils/wistia-embed.js +0 -138
- package/src/lib/app-utils/wistia-embed.js.map +0 -1
- package/src/lib/app-utils/workflows.d.ts +0 -136
- package/src/lib/app-utils/workflows.js +0 -346
- package/src/lib/app-utils/workflows.js.map +0 -1
- package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
- package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
- package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
|
@@ -42,7 +42,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
42
42
|
*
|
|
43
43
|
* `localStorage`, expiring at {@link ATTRIBUTION_WINDOW_DAYS}.
|
|
44
44
|
*
|
|
45
|
-
* `
|
|
45
|
+
* `utm-forwarding.ts` holds its own campaign in `sessionStorage` and
|
|
46
46
|
* argues for it: a first touch that outlived the visit would start
|
|
47
47
|
* attributing next week's organic return to this week's ad. That argument is
|
|
48
48
|
* correct for the question it answers — which campaign produced THIS SIGNUP,
|
|
@@ -64,7 +64,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
64
64
|
* The revenue join credits the LAST click, and a product where a lead and an
|
|
65
65
|
* order attribute by different rules is worse than one that is uniformly
|
|
66
66
|
* approximate. So a new campaign arrival OVERWRITES the stored one. This is
|
|
67
|
-
* the deliberate opposite of {@link
|
|
67
|
+
* the deliberate opposite of {@link rememberVisitUtm}, which keeps the
|
|
68
68
|
* first — that one describes a single visit's origin and this one describes
|
|
69
69
|
* which campaign most recently brought a person back.
|
|
70
70
|
*
|
|
@@ -104,7 +104,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
104
104
|
* ## What is never carried
|
|
105
105
|
*
|
|
106
106
|
* Only the three allowlisted `utm_` labels, scrubbed by
|
|
107
|
-
* {@link
|
|
107
|
+
* {@link parseUtmAttribution} — which refuses an email-shaped value
|
|
108
108
|
* outright. No address, no person's identifier, no click id, nothing that
|
|
109
109
|
* names the visitor. A campaign link is exactly where putting a recipient in
|
|
110
110
|
* a query string would be tempting, and the parser is what makes it
|
|
@@ -119,7 +119,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
119
119
|
* recorded server-side against the recipient's address hash. The labels below
|
|
120
120
|
* are the WEB channel's touch, and a campaign that carries none is joined by
|
|
121
121
|
* address instead.
|
|
122
|
-
*/ import {
|
|
122
|
+
*/ import { utmAttributionQuery, parseUtmAttribution } from "./utm-attribution.js";
|
|
123
123
|
/*==========================================
|
|
124
124
|
* THE WINDOW, AND WHY THIS IS THE SECOND COPY OF A NUMBER.
|
|
125
125
|
*
|
|
@@ -183,7 +183,7 @@ function localStore() {
|
|
|
183
183
|
* cannot drift from what {@link parseCampaignTouch} reads back.
|
|
184
184
|
*/ export function campaignTouchWire(touch) {
|
|
185
185
|
if (!touch) return '';
|
|
186
|
-
const labels =
|
|
186
|
+
const labels = utmAttributionQuery(touch);
|
|
187
187
|
if (!labels) return '';
|
|
188
188
|
const atMs = Math.round(Number(touch.atMs));
|
|
189
189
|
if (!Number.isFinite(atMs) || atMs <= 0) return '';
|
|
@@ -205,7 +205,7 @@ function localStore() {
|
|
|
205
205
|
} catch (unused) {
|
|
206
206
|
return null;
|
|
207
207
|
}
|
|
208
|
-
const campaign =
|
|
208
|
+
const campaign = parseUtmAttribution(params);
|
|
209
209
|
if (!campaign) return null;
|
|
210
210
|
const atMs = Number(params.get(CAMPAIGN_TOUCH_TIME_KEY));
|
|
211
211
|
if (!touchIsInWindow(atMs, nowMs)) return null;
|
|
@@ -249,7 +249,7 @@ function localStore() {
|
|
|
249
249
|
const store = localStore();
|
|
250
250
|
if (!store) return null;
|
|
251
251
|
const source = typeof search === 'string' ? search : typeof window === 'undefined' ? '' : window.location.search;
|
|
252
|
-
const campaign =
|
|
252
|
+
const campaign = parseUtmAttribution(new URLSearchParams(source));
|
|
253
253
|
if (!campaign) return null;
|
|
254
254
|
const touch = _extends({}, campaign, {
|
|
255
255
|
atMs: nowMs
|
|
@@ -282,7 +282,7 @@ function localStore() {
|
|
|
282
282
|
* device would be keeping a record of where somebody came from for no purpose
|
|
283
283
|
* anything reads. The read that finds it removes it.
|
|
284
284
|
*/ export function readCampaignTouch(nowMs = Date.now()) {
|
|
285
|
-
const live = typeof window === 'undefined' ? null :
|
|
285
|
+
const live = typeof window === 'undefined' ? null : parseUtmAttribution(new URLSearchParams(window.location.search));
|
|
286
286
|
if (live) return _extends({}, live, {
|
|
287
287
|
atMs: nowMs
|
|
288
288
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/campaign-touch.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 VISITOR'S HALF OF CAMPAIGN ATTRIBUTION — the touch that has to survive\n * the gap between arriving from a campaign and becoming somebody.\n *\n * ## The gap this exists to cross\n *\n * A known recipient can be joined on their address: they clicked a campaign's\n * mail, the delivery webhook stamped the click on their person document, and\n * `email-revenue-attribution.ts` reads it back when their order settles. An\n * ANONYMOUS visitor has no such handle. They arrive from an ad, a partner\n * link or a social post, browse for a while, and only become identifiable at\n * the moment they submit a form, sign up, book or check out. Between the\n * arrival and that moment there is nothing on the server that connects them.\n *\n * The site's own collector already reads `utm_source`/`utm_medium`/\n * `utm_campaign` off the landing URL and increments a per-day label counter\n * with them. That answers \"how many views did this campaign send us\" and\n * stops there — the labels are never carried to the moment the visitor says\n * who they are, so every form, lead, contact and booking a campaign caused is\n * recorded as though nobody caused it.\n *\n * This carries them. The touch is held on the VISITOR, and every identify\n * moment attaches it.\n *\n * ## What holds it, and why it outlives the tab\n *\n * `localStorage`, expiring at {@link ATTRIBUTION_WINDOW_DAYS}.\n *\n * `campaign-forwarding.ts` holds its own campaign in `sessionStorage` and\n * argues for it: a first touch that outlived the visit would start\n * attributing next week's organic return to this week's ad. That argument is\n * correct for the question it answers — which campaign produced THIS SIGNUP,\n * asked of one uninterrupted visit to the marketing site — and it is the\n * wrong shape here, because this question already has a stated answer to\n * \"how long may a touch be credited\": the attribution window, seven days,\n * stamped onto every record so the rule is readable off the data.\n *\n * A store that dies with the tab cannot express a seven-day window. It would\n * credit only the visitors who convert without ever closing the tab and\n * silently record every other conversion as organic — a measured zero, which\n * is the failure mode this whole area exists to end. So the touch lives as\n * long as the window says it may and not one millisecond longer: an expired\n * entry is DELETED on the read that finds it, rather than ignored, so a\n * device never holds a campaign it can no longer be credited with.\n *\n * ## Last touch, not first\n *\n * The revenue join credits the LAST click, and a product where a lead and an\n * order attribute by different rules is worse than one that is uniformly\n * approximate. So a new campaign arrival OVERWRITES the stored one. This is\n * the deliberate opposite of {@link rememberVisitCampaign}, which keeps the\n * first — that one describes a single visit's origin and this one describes\n * which campaign most recently brought a person back.\n *\n * ## Consent\n *\n * Two tiers, exactly as the console hop splits them, and the split is the\n * same because the acts are the same:\n *\n * - **The live URL at the moment of conversion** touches no storage. The\n * parameters are already in the page the visitor asked for, nothing is\n * written to their device, and there is accordingly nothing to consent to.\n * A visitor who converts on the page the ad landed them on is attributed\n * with no grant of any kind.\n * - **The remembered touch** is written to the visitor's device for an\n * analytics purpose, which is `analytics_storage`, and it waits for the\n * same grant the analytics tag waits for. The gate is not re-derived here:\n * the caller already computed it for the tag and hands the same boolean\n * down, so there is one gate and it cannot drift from itself.\n *\n * `null` is UNRESOLVED and is not `false`. With `strictNullChecks` off\n * repo-wide that distinction evaporates unless it is carried explicitly, so\n * it is: until the visitor's state is actually settled, nothing is read and\n * nothing is written. Failing closed costs an attribution; failing open\n * writes to a device before the visitor answered.\n *\n * On a WITHDRAWAL the stored touch is removed rather than merely ignored.\n * Somebody who changes their mind should not leave the thing they withdrew\n * consent for sitting on their device.\n *\n * ⛔ **A host whose posture requires prior consent and whose visitor has not\n * given it produces no remembered touch at all.** Their conversion attributes\n * to nothing, exactly as direct traffic does. That is the degradation, stated\n * rather than worked around: there is no fallback identifier, no fingerprint\n * and no server-side cookie, because every one of those is the durable\n * identifier the analytics posture on this runtime deliberately does not set.\n *\n * ## What is never carried\n *\n * Only the three allowlisted `utm_` labels, scrubbed by\n * {@link parseCampaignAttribution} — which refuses an email-shaped value\n * outright. No address, no person's identifier, no click id, nothing that\n * names the visitor. A campaign link is exactly where putting a recipient in\n * a query string would be tempting, and the parser is what makes it\n * impossible rather than merely discouraged: the stored string is re-parsed\n * through the same allowlist that wrote it, so a hand-edited `localStorage`\n * entry can claim no more than a hand-edited URL could.\n *\n * ⛔ **Nothing here depends on a parameter surviving the mail provider's\n * click wrapper.** A campaign link is rewritten by the provider and redirects\n * to the authored URL, so whatever the marketer put on it arrives — but the\n * email channel does not rely on that, because an email click is already\n * recorded server-side against the recipient's address hash. The labels below\n * are the WEB channel's touch, and a campaign that carries none is joined by\n * address instead.\n */\n\nimport {\n campaignAttributionQuery,\n parseCampaignAttribution,\n type CampaignAttribution,\n} from './campaign-attribution'\n\n/*==========================================\n * THE WINDOW, AND WHY THIS IS THE SECOND COPY OF A NUMBER.\n *\n * `email-revenue-window.ts` in `shared-util-email` declares the same seven\n * days, and its docblock is right that a window defined twice is a window\n * that drifts. Importing it here is not available: `shared-util-email` is\n * tagged `scope:shared` and reaches back into `@aglyn/aglyn`, so an edge in\n * this direction closes a project cycle and the module-boundary rule refuses\n * it. That is the same wall `email-media-src.ts` hit, and the answer is the\n * same one it took — a deliberate copy, pinned by a drift guard in an app\n * spec that can import both sides (`attribution-window-drift.spec.ts`).\n *\n * The copy is the WINDOW only. The model name is stamped onto records, which\n * happens server-side, so it stays in one place and never comes near this\n * file.\n *=========================================*/\n\n/** Days between a campaign touch and a conversion it may be credited with. */\nexport const ATTRIBUTION_WINDOW_DAYS = 7\n\n/** The same window in milliseconds. */\nexport const ATTRIBUTION_WINDOW_MS =\n ATTRIBUTION_WINDOW_DAYS * 24 * 60 * 60 * 1000\n\n/**\n * Whether a touch may be credited with a conversion at `convertedAtMs`.\n *\n * Both bounds matter and they fail differently, exactly as the revenue join\n * describes them: a touch AFTER the conversion is the receipt rather than the\n * cause, and a touch older than the window is one nobody can argue caused\n * anything. Inclusive at both ends.\n */\nfunction touchIsInWindow(touchedAtMs: number, convertedAtMs: number): boolean {\n if (!Number.isFinite(touchedAtMs) || !Number.isFinite(convertedAtMs)) {\n return false\n }\n if (touchedAtMs <= 0 || convertedAtMs <= 0) return false\n const age = convertedAtMs - touchedAtMs\n return age >= 0 && age <= ATTRIBUTION_WINDOW_MS\n}\n\n/** Where the touch is held. Namespaced like every other key this app sets. */\nexport const CAMPAIGN_TOUCH_STORAGE_KEY = 'aglyn:campaign-touch'\n\n/**\n * The parameter the touch's instant rides under inside the wire form.\n *\n * Deliberately not a `utm_` name: the wire form is parsed by the same\n * allowlist that reads a URL, and a fourth `utm_` key would either be dropped\n * by it or have to widen it. `t` is outside the allowlist and read\n * separately.\n */\nexport const CAMPAIGN_TOUCH_TIME_KEY = 't'\n\n/** A campaign the visitor arrived from, and when they arrived from it. */\nexport interface CampaignTouch extends CampaignAttribution {\n /** When the visitor followed the campaign link, epoch ms. */\n atMs: number\n}\n\n/**\n * Whether the visitor has granted analytics storage.\n *\n * `null` means NOT YET RESOLVED and is not the same as `false` — see the\n * consent section above.\n */\nlet storageConsent: boolean | null = null\n\nfunction localStore(): Storage | null {\n if (typeof window === 'undefined') return null\n // Touching the property itself throws in some privacy modes, not just its\n // methods.\n try {\n return window.localStorage ?? null\n } catch {\n return null\n }\n}\n\n/**\n * The canonical stored form of a touch — the ONLY place it is written, so it\n * cannot drift from what {@link parseCampaignTouch} reads back.\n */\nexport function campaignTouchWire(touch: CampaignTouch | null | undefined): string {\n if (!touch) return ''\n const labels = campaignAttributionQuery(touch)\n if (!labels) return ''\n const atMs = Math.round(Number(touch.atMs))\n if (!Number.isFinite(atMs) || atMs <= 0) return ''\n return `${labels}&${CAMPAIGN_TOUCH_TIME_KEY}=${atMs}`\n}\n\n/**\n * Read a stored or transmitted touch back, or `null`.\n *\n * The window is enforced HERE rather than at the call sites, so every reader\n * — the browser deciding what to send, the server deciding what to credit —\n * agrees about what an expired touch is: nothing at all. A touch dated in the\n * future is refused for the reason the revenue join refuses one: a click\n * after the conversion is the receipt, not the cause.\n */\nexport function parseCampaignTouch(\n wire: unknown,\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n if (typeof wire !== 'string' || !wire) return null\n let params: URLSearchParams\n try {\n params = new URLSearchParams(wire)\n } catch {\n return null\n }\n const campaign = parseCampaignAttribution(params)\n if (!campaign) return null\n const atMs = Number(params.get(CAMPAIGN_TOUCH_TIME_KEY))\n if (!touchIsInWindow(atMs, nowMs)) return null\n return { ...campaign, atMs }\n}\n\n/**\n * Tell this module what the visitor's analytics consent state is.\n *\n * Called by whoever already computed it for the tag, on every render, so a\n * grant remembers the arrival the moment it is given and a withdrawal drops\n * the stored touch immediately.\n */\nexport function setCampaignTouchConsent(allowed: boolean | null): void {\n storageConsent = allowed === true ? true : allowed === false ? false : null\n if (storageConsent === true) {\n rememberCampaignTouch()\n return\n }\n if (storageConsent === false) {\n const store = localStore()\n if (!store) return\n try {\n store.removeItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n // Nothing else to try, and a failed cleanup must not break the page.\n }\n }\n}\n\n/**\n * Remember the campaign on the current URL as this visitor's latest touch.\n *\n * Overwrites, because the model is last touch. A URL naming no campaign\n * writes NOTHING and clears nothing: a visitor who arrives from an ad and\n * then browses ten organic pages has one touch, not one touch erased by the\n * second pageview.\n *\n * @returns what is now remembered, or `null`.\n */\nexport function rememberCampaignTouch(\n search?: string,\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n if (storageConsent !== true) return null\n const store = localStore()\n if (!store) return null\n const source =\n typeof search === 'string'\n ? search\n : typeof window === 'undefined'\n ? ''\n : window.location.search\n const campaign = parseCampaignAttribution(new URLSearchParams(source))\n if (!campaign) return null\n const touch: CampaignTouch = { ...campaign, atMs: nowMs }\n const wire = campaignTouchWire(touch)\n if (!wire) return null\n try {\n store.setItem(CAMPAIGN_TOUCH_STORAGE_KEY, wire)\n } catch {\n // A store that refuses the write costs the walk from the landing page to\n // the conversion, never a conversion on the landing page itself — the\n // live URL below still carries that visitor.\n return null\n }\n return touch\n}\n\n/**\n * The touch to credit a conversion happening right now, or `null`.\n *\n * ## The order is the model\n *\n * The live URL is consulted FIRST and wins whenever it names a campaign,\n * because a campaign on the address bar at the moment of conversion is by\n * definition the most recent touch there is. Only when the current page names\n * none does this fall back to what was remembered.\n *\n * ## An expired entry is deleted, not skipped\n *\n * A touch past the window can never be credited again, so leaving it on the\n * device would be keeping a record of where somebody came from for no purpose\n * anything reads. The read that finds it removes it.\n */\nexport function readCampaignTouch(\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n const live =\n typeof window === 'undefined'\n ? null\n : parseCampaignAttribution(new URLSearchParams(window.location.search))\n if (live) return { ...live, atMs: nowMs }\n\n if (storageConsent !== true) return null\n const store = localStore()\n if (!store) return null\n let raw: string | null\n try {\n raw = store.getItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n return null\n }\n if (!raw) return null\n const touch = parseCampaignTouch(raw, nowMs)\n if (!touch) {\n try {\n store.removeItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n // The entry stays until the next read finds it again; it is already\n // uncreditable, so nothing downstream is affected.\n }\n return null\n }\n return touch\n}\n\n/**\n * The fragment a conversion request spreads into its body, or `{}`.\n *\n * One helper rather than four hand-written spreads: a door that forgets it\n * reports its conversions as organic, which looks exactly like a campaign\n * that produced none. Empty when there is no touch — never a placeholder,\n * never `campaignTouch: undefined`, so that \"arrived from nowhere\" and \"this\n * door does not report\" stay distinguishable on the wire.\n */\nexport function campaignTouchField(\n nowMs: number = Date.now(),\n): { campaignTouch?: string } {\n const wire = campaignTouchWire(readCampaignTouch(nowMs))\n return wire ? { campaignTouch: wire } : {}\n}\n"],"names":["campaignAttributionQuery","parseCampaignAttribution","ATTRIBUTION_WINDOW_DAYS","ATTRIBUTION_WINDOW_MS","touchIsInWindow","touchedAtMs","convertedAtMs","Number","isFinite","age","CAMPAIGN_TOUCH_STORAGE_KEY","CAMPAIGN_TOUCH_TIME_KEY","storageConsent","localStore","window","localStorage","campaignTouchWire","touch","labels","atMs","Math","round","parseCampaignTouch","wire","nowMs","Date","now","params","URLSearchParams","campaign","get","setCampaignTouchConsent","allowed","rememberCampaignTouch","store","removeItem","search","source","location","setItem","readCampaignTouch","live","raw","getItem","campaignTouchField","campaignTouch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/campaign-touch.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 VISITOR'S HALF OF CAMPAIGN ATTRIBUTION — the touch that has to survive\n * the gap between arriving from a campaign and becoming somebody.\n *\n * ## The gap this exists to cross\n *\n * A known recipient can be joined on their address: they clicked a campaign's\n * mail, the delivery webhook stamped the click on their person document, and\n * `email-revenue-attribution.ts` reads it back when their order settles. An\n * ANONYMOUS visitor has no such handle. They arrive from an ad, a partner\n * link or a social post, browse for a while, and only become identifiable at\n * the moment they submit a form, sign up, book or check out. Between the\n * arrival and that moment there is nothing on the server that connects them.\n *\n * The site's own collector already reads `utm_source`/`utm_medium`/\n * `utm_campaign` off the landing URL and increments a per-day label counter\n * with them. That answers \"how many views did this campaign send us\" and\n * stops there — the labels are never carried to the moment the visitor says\n * who they are, so every form, lead, contact and booking a campaign caused is\n * recorded as though nobody caused it.\n *\n * This carries them. The touch is held on the VISITOR, and every identify\n * moment attaches it.\n *\n * ## What holds it, and why it outlives the tab\n *\n * `localStorage`, expiring at {@link ATTRIBUTION_WINDOW_DAYS}.\n *\n * `utm-forwarding.ts` holds its own campaign in `sessionStorage` and\n * argues for it: a first touch that outlived the visit would start\n * attributing next week's organic return to this week's ad. That argument is\n * correct for the question it answers — which campaign produced THIS SIGNUP,\n * asked of one uninterrupted visit to the marketing site — and it is the\n * wrong shape here, because this question already has a stated answer to\n * \"how long may a touch be credited\": the attribution window, seven days,\n * stamped onto every record so the rule is readable off the data.\n *\n * A store that dies with the tab cannot express a seven-day window. It would\n * credit only the visitors who convert without ever closing the tab and\n * silently record every other conversion as organic — a measured zero, which\n * is the failure mode this whole area exists to end. So the touch lives as\n * long as the window says it may and not one millisecond longer: an expired\n * entry is DELETED on the read that finds it, rather than ignored, so a\n * device never holds a campaign it can no longer be credited with.\n *\n * ## Last touch, not first\n *\n * The revenue join credits the LAST click, and a product where a lead and an\n * order attribute by different rules is worse than one that is uniformly\n * approximate. So a new campaign arrival OVERWRITES the stored one. This is\n * the deliberate opposite of {@link rememberVisitUtm}, which keeps the\n * first — that one describes a single visit's origin and this one describes\n * which campaign most recently brought a person back.\n *\n * ## Consent\n *\n * Two tiers, exactly as the console hop splits them, and the split is the\n * same because the acts are the same:\n *\n * - **The live URL at the moment of conversion** touches no storage. The\n * parameters are already in the page the visitor asked for, nothing is\n * written to their device, and there is accordingly nothing to consent to.\n * A visitor who converts on the page the ad landed them on is attributed\n * with no grant of any kind.\n * - **The remembered touch** is written to the visitor's device for an\n * analytics purpose, which is `analytics_storage`, and it waits for the\n * same grant the analytics tag waits for. The gate is not re-derived here:\n * the caller already computed it for the tag and hands the same boolean\n * down, so there is one gate and it cannot drift from itself.\n *\n * `null` is UNRESOLVED and is not `false`. With `strictNullChecks` off\n * repo-wide that distinction evaporates unless it is carried explicitly, so\n * it is: until the visitor's state is actually settled, nothing is read and\n * nothing is written. Failing closed costs an attribution; failing open\n * writes to a device before the visitor answered.\n *\n * On a WITHDRAWAL the stored touch is removed rather than merely ignored.\n * Somebody who changes their mind should not leave the thing they withdrew\n * consent for sitting on their device.\n *\n * ⛔ **A host whose posture requires prior consent and whose visitor has not\n * given it produces no remembered touch at all.** Their conversion attributes\n * to nothing, exactly as direct traffic does. That is the degradation, stated\n * rather than worked around: there is no fallback identifier, no fingerprint\n * and no server-side cookie, because every one of those is the durable\n * identifier the analytics posture on this runtime deliberately does not set.\n *\n * ## What is never carried\n *\n * Only the three allowlisted `utm_` labels, scrubbed by\n * {@link parseUtmAttribution} — which refuses an email-shaped value\n * outright. No address, no person's identifier, no click id, nothing that\n * names the visitor. A campaign link is exactly where putting a recipient in\n * a query string would be tempting, and the parser is what makes it\n * impossible rather than merely discouraged: the stored string is re-parsed\n * through the same allowlist that wrote it, so a hand-edited `localStorage`\n * entry can claim no more than a hand-edited URL could.\n *\n * ⛔ **Nothing here depends on a parameter surviving the mail provider's\n * click wrapper.** A campaign link is rewritten by the provider and redirects\n * to the authored URL, so whatever the marketer put on it arrives — but the\n * email channel does not rely on that, because an email click is already\n * recorded server-side against the recipient's address hash. The labels below\n * are the WEB channel's touch, and a campaign that carries none is joined by\n * address instead.\n */\n\nimport {\n utmAttributionQuery,\n parseUtmAttribution,\n type UtmAttribution,\n} from './utm-attribution'\n\n/*==========================================\n * THE WINDOW, AND WHY THIS IS THE SECOND COPY OF A NUMBER.\n *\n * `email-revenue-window.ts` in `shared-util-email` declares the same seven\n * days, and its docblock is right that a window defined twice is a window\n * that drifts. Importing it here is not available: `shared-util-email` is\n * tagged `scope:shared` and reaches back into `@aglyn/aglyn`, so an edge in\n * this direction closes a project cycle and the module-boundary rule refuses\n * it. That is the same wall `email-media-src.ts` hit, and the answer is the\n * same one it took — a deliberate copy, pinned by a drift guard in an app\n * spec that can import both sides (`attribution-window-drift.spec.ts`).\n *\n * The copy is the WINDOW only. The model name is stamped onto records, which\n * happens server-side, so it stays in one place and never comes near this\n * file.\n *=========================================*/\n\n/** Days between a campaign touch and a conversion it may be credited with. */\nexport const ATTRIBUTION_WINDOW_DAYS = 7\n\n/** The same window in milliseconds. */\nexport const ATTRIBUTION_WINDOW_MS =\n ATTRIBUTION_WINDOW_DAYS * 24 * 60 * 60 * 1000\n\n/**\n * Whether a touch may be credited with a conversion at `convertedAtMs`.\n *\n * Both bounds matter and they fail differently, exactly as the revenue join\n * describes them: a touch AFTER the conversion is the receipt rather than the\n * cause, and a touch older than the window is one nobody can argue caused\n * anything. Inclusive at both ends.\n */\nfunction touchIsInWindow(touchedAtMs: number, convertedAtMs: number): boolean {\n if (!Number.isFinite(touchedAtMs) || !Number.isFinite(convertedAtMs)) {\n return false\n }\n if (touchedAtMs <= 0 || convertedAtMs <= 0) return false\n const age = convertedAtMs - touchedAtMs\n return age >= 0 && age <= ATTRIBUTION_WINDOW_MS\n}\n\n/** Where the touch is held. Namespaced like every other key this app sets. */\nexport const CAMPAIGN_TOUCH_STORAGE_KEY = 'aglyn:campaign-touch'\n\n/**\n * The parameter the touch's instant rides under inside the wire form.\n *\n * Deliberately not a `utm_` name: the wire form is parsed by the same\n * allowlist that reads a URL, and a fourth `utm_` key would either be dropped\n * by it or have to widen it. `t` is outside the allowlist and read\n * separately.\n */\nexport const CAMPAIGN_TOUCH_TIME_KEY = 't'\n\n/** A campaign the visitor arrived from, and when they arrived from it. */\nexport interface CampaignTouch extends UtmAttribution {\n /** When the visitor followed the campaign link, epoch ms. */\n atMs: number\n}\n\n/**\n * Whether the visitor has granted analytics storage.\n *\n * `null` means NOT YET RESOLVED and is not the same as `false` — see the\n * consent section above.\n */\nlet storageConsent: boolean | null = null\n\nfunction localStore(): Storage | null {\n if (typeof window === 'undefined') return null\n // Touching the property itself throws in some privacy modes, not just its\n // methods.\n try {\n return window.localStorage ?? null\n } catch {\n return null\n }\n}\n\n/**\n * The canonical stored form of a touch — the ONLY place it is written, so it\n * cannot drift from what {@link parseCampaignTouch} reads back.\n */\nexport function campaignTouchWire(touch: CampaignTouch | null | undefined): string {\n if (!touch) return ''\n const labels = utmAttributionQuery(touch)\n if (!labels) return ''\n const atMs = Math.round(Number(touch.atMs))\n if (!Number.isFinite(atMs) || atMs <= 0) return ''\n return `${labels}&${CAMPAIGN_TOUCH_TIME_KEY}=${atMs}`\n}\n\n/**\n * Read a stored or transmitted touch back, or `null`.\n *\n * The window is enforced HERE rather than at the call sites, so every reader\n * — the browser deciding what to send, the server deciding what to credit —\n * agrees about what an expired touch is: nothing at all. A touch dated in the\n * future is refused for the reason the revenue join refuses one: a click\n * after the conversion is the receipt, not the cause.\n */\nexport function parseCampaignTouch(\n wire: unknown,\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n if (typeof wire !== 'string' || !wire) return null\n let params: URLSearchParams\n try {\n params = new URLSearchParams(wire)\n } catch {\n return null\n }\n const campaign = parseUtmAttribution(params)\n if (!campaign) return null\n const atMs = Number(params.get(CAMPAIGN_TOUCH_TIME_KEY))\n if (!touchIsInWindow(atMs, nowMs)) return null\n return { ...campaign, atMs }\n}\n\n/**\n * Tell this module what the visitor's analytics consent state is.\n *\n * Called by whoever already computed it for the tag, on every render, so a\n * grant remembers the arrival the moment it is given and a withdrawal drops\n * the stored touch immediately.\n */\nexport function setCampaignTouchConsent(allowed: boolean | null): void {\n storageConsent = allowed === true ? true : allowed === false ? false : null\n if (storageConsent === true) {\n rememberCampaignTouch()\n return\n }\n if (storageConsent === false) {\n const store = localStore()\n if (!store) return\n try {\n store.removeItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n // Nothing else to try, and a failed cleanup must not break the page.\n }\n }\n}\n\n/**\n * Remember the campaign on the current URL as this visitor's latest touch.\n *\n * Overwrites, because the model is last touch. A URL naming no campaign\n * writes NOTHING and clears nothing: a visitor who arrives from an ad and\n * then browses ten organic pages has one touch, not one touch erased by the\n * second pageview.\n *\n * @returns what is now remembered, or `null`.\n */\nexport function rememberCampaignTouch(\n search?: string,\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n if (storageConsent !== true) return null\n const store = localStore()\n if (!store) return null\n const source =\n typeof search === 'string'\n ? search\n : typeof window === 'undefined'\n ? ''\n : window.location.search\n const campaign = parseUtmAttribution(new URLSearchParams(source))\n if (!campaign) return null\n const touch: CampaignTouch = { ...campaign, atMs: nowMs }\n const wire = campaignTouchWire(touch)\n if (!wire) return null\n try {\n store.setItem(CAMPAIGN_TOUCH_STORAGE_KEY, wire)\n } catch {\n // A store that refuses the write costs the walk from the landing page to\n // the conversion, never a conversion on the landing page itself — the\n // live URL below still carries that visitor.\n return null\n }\n return touch\n}\n\n/**\n * The touch to credit a conversion happening right now, or `null`.\n *\n * ## The order is the model\n *\n * The live URL is consulted FIRST and wins whenever it names a campaign,\n * because a campaign on the address bar at the moment of conversion is by\n * definition the most recent touch there is. Only when the current page names\n * none does this fall back to what was remembered.\n *\n * ## An expired entry is deleted, not skipped\n *\n * A touch past the window can never be credited again, so leaving it on the\n * device would be keeping a record of where somebody came from for no purpose\n * anything reads. The read that finds it removes it.\n */\nexport function readCampaignTouch(\n nowMs: number = Date.now(),\n): CampaignTouch | null {\n const live =\n typeof window === 'undefined'\n ? null\n : parseUtmAttribution(new URLSearchParams(window.location.search))\n if (live) return { ...live, atMs: nowMs }\n\n if (storageConsent !== true) return null\n const store = localStore()\n if (!store) return null\n let raw: string | null\n try {\n raw = store.getItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n return null\n }\n if (!raw) return null\n const touch = parseCampaignTouch(raw, nowMs)\n if (!touch) {\n try {\n store.removeItem(CAMPAIGN_TOUCH_STORAGE_KEY)\n } catch {\n // The entry stays until the next read finds it again; it is already\n // uncreditable, so nothing downstream is affected.\n }\n return null\n }\n return touch\n}\n\n/**\n * The fragment a conversion request spreads into its body, or `{}`.\n *\n * One helper rather than four hand-written spreads: a door that forgets it\n * reports its conversions as organic, which looks exactly like a campaign\n * that produced none. Empty when there is no touch — never a placeholder,\n * never `campaignTouch: undefined`, so that \"arrived from nowhere\" and \"this\n * door does not report\" stay distinguishable on the wire.\n */\nexport function campaignTouchField(\n nowMs: number = Date.now(),\n): { campaignTouch?: string } {\n const wire = campaignTouchWire(readCampaignTouch(nowMs))\n return wire ? { campaignTouch: wire } : {}\n}\n"],"names":["utmAttributionQuery","parseUtmAttribution","ATTRIBUTION_WINDOW_DAYS","ATTRIBUTION_WINDOW_MS","touchIsInWindow","touchedAtMs","convertedAtMs","Number","isFinite","age","CAMPAIGN_TOUCH_STORAGE_KEY","CAMPAIGN_TOUCH_TIME_KEY","storageConsent","localStore","window","localStorage","campaignTouchWire","touch","labels","atMs","Math","round","parseCampaignTouch","wire","nowMs","Date","now","params","URLSearchParams","campaign","get","setCampaignTouchConsent","allowed","rememberCampaignTouch","store","removeItem","search","source","location","setItem","readCampaignTouch","live","raw","getItem","campaignTouchField","campaignTouch"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyGC,GAED,SACEA,mBAAmB,EACnBC,mBAAmB,QAEd,uBAAmB;AAE1B;;;;;;;;;;;;;;;2CAe2C,GAE3C,4EAA4E,GAC5E,OAAO,MAAMC,0BAA0B,EAAC;AAExC,qCAAqC,GACrC,OAAO,MAAMC,wBACXD,0BAA0B,KAAK,KAAK,KAAK,KAAI;AAE/C;;;;;;;CAOC,GACD,SAASE,gBAAgBC,WAAmB,EAAEC,aAAqB;IACjE,IAAI,CAACC,OAAOC,QAAQ,CAACH,gBAAgB,CAACE,OAAOC,QAAQ,CAACF,gBAAgB;QACpE,OAAO;IACT;IACA,IAAID,eAAe,KAAKC,iBAAiB,GAAG,OAAO;IACnD,MAAMG,MAAMH,gBAAgBD;IAC5B,OAAOI,OAAO,KAAKA,OAAON;AAC5B;AAEA,4EAA4E,GAC5E,OAAO,MAAMO,6BAA6B,uBAAsB;AAEhE;;;;;;;CAOC,GACD,OAAO,MAAMC,0BAA0B,IAAG;AAQ1C;;;;;CAKC,GACD,IAAIC,iBAAiC;AAErC,SAASC;IACP,IAAI,OAAOC,WAAW,aAAa,OAAO;IAC1C,0EAA0E;IAC1E,WAAW;IACX,IAAI;YACKA;QAAP,QAAOA,uBAAAA,OAAOC,YAAY,YAAnBD,uBAAuB;IAChC,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,SAASE,kBAAkBC,KAAuC;IACvE,IAAI,CAACA,OAAO,OAAO;IACnB,MAAMC,SAASlB,oBAAoBiB;IACnC,IAAI,CAACC,QAAQ,OAAO;IACpB,MAAMC,OAAOC,KAAKC,KAAK,CAACd,OAAOU,MAAME,IAAI;IACzC,IAAI,CAACZ,OAAOC,QAAQ,CAACW,SAASA,QAAQ,GAAG,OAAO;IAChD,OAAO,GAAGD,OAAO,CAAC,EAAEP,wBAAwB,CAAC,EAAEQ,MAAM;AACvD;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASG,mBACdC,IAAa,EACbC,QAAgBC,KAAKC,GAAG,EAAE;IAE1B,IAAI,OAAOH,SAAS,YAAY,CAACA,MAAM,OAAO;IAC9C,IAAII;IACJ,IAAI;QACFA,SAAS,IAAIC,gBAAgBL;IAC/B,EAAE,eAAM;QACN,OAAO;IACT;IACA,MAAMM,WAAW5B,oBAAoB0B;IACrC,IAAI,CAACE,UAAU,OAAO;IACtB,MAAMV,OAAOZ,OAAOoB,OAAOG,GAAG,CAACnB;IAC/B,IAAI,CAACP,gBAAgBe,MAAMK,QAAQ,OAAO;IAC1C,OAAO,aAAKK;QAAUV;;AACxB;AAEA;;;;;;CAMC,GACD,OAAO,SAASY,wBAAwBC,OAAuB;IAC7DpB,iBAAiBoB,YAAY,OAAO,OAAOA,YAAY,QAAQ,QAAQ;IACvE,IAAIpB,mBAAmB,MAAM;QAC3BqB;QACA;IACF;IACA,IAAIrB,mBAAmB,OAAO;QAC5B,MAAMsB,QAAQrB;QACd,IAAI,CAACqB,OAAO;QACZ,IAAI;YACFA,MAAMC,UAAU,CAACzB;QACnB,EAAE,eAAM;QACN,qEAAqE;QACvE;IACF;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASuB,sBACdG,MAAe,EACfZ,QAAgBC,KAAKC,GAAG,EAAE;IAE1B,IAAId,mBAAmB,MAAM,OAAO;IACpC,MAAMsB,QAAQrB;IACd,IAAI,CAACqB,OAAO,OAAO;IACnB,MAAMG,SACJ,OAAOD,WAAW,WACdA,SACA,OAAOtB,WAAW,cAChB,KACAA,OAAOwB,QAAQ,CAACF,MAAM;IAC9B,MAAMP,WAAW5B,oBAAoB,IAAI2B,gBAAgBS;IACzD,IAAI,CAACR,UAAU,OAAO;IACtB,MAAMZ,QAAuB,aAAKY;QAAUV,MAAMK;;IAClD,MAAMD,OAAOP,kBAAkBC;IAC/B,IAAI,CAACM,MAAM,OAAO;IAClB,IAAI;QACFW,MAAMK,OAAO,CAAC7B,4BAA4Ba;IAC5C,EAAE,eAAM;QACN,yEAAyE;QACzE,sEAAsE;QACtE,6CAA6C;QAC7C,OAAO;IACT;IACA,OAAON;AACT;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASuB,kBACdhB,QAAgBC,KAAKC,GAAG,EAAE;IAE1B,MAAMe,OACJ,OAAO3B,WAAW,cACd,OACAb,oBAAoB,IAAI2B,gBAAgBd,OAAOwB,QAAQ,CAACF,MAAM;IACpE,IAAIK,MAAM,OAAO,aAAKA;QAAMtB,MAAMK;;IAElC,IAAIZ,mBAAmB,MAAM,OAAO;IACpC,MAAMsB,QAAQrB;IACd,IAAI,CAACqB,OAAO,OAAO;IACnB,IAAIQ;IACJ,IAAI;QACFA,MAAMR,MAAMS,OAAO,CAACjC;IACtB,EAAE,eAAM;QACN,OAAO;IACT;IACA,IAAI,CAACgC,KAAK,OAAO;IACjB,MAAMzB,QAAQK,mBAAmBoB,KAAKlB;IACtC,IAAI,CAACP,OAAO;QACV,IAAI;YACFiB,MAAMC,UAAU,CAACzB;QACnB,EAAE,eAAM;QACN,oEAAoE;QACpE,mDAAmD;QACrD;QACA,OAAO;IACT;IACA,OAAOO;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAAS2B,mBACdpB,QAAgBC,KAAKC,GAAG,EAAE;IAE1B,MAAMH,OAAOP,kBAAkBwB,kBAAkBhB;IACjD,OAAOD,OAAO;QAAEsB,eAAetB;IAAK,IAAI,CAAC;AAC3C"}
|
|
@@ -306,8 +306,8 @@ export interface CollectionEntryRecord {
|
|
|
306
306
|
coverImage?: string;
|
|
307
307
|
/**
|
|
308
308
|
* The entry's featured video (AGL-2956), stored in the shape
|
|
309
|
-
* {@link coverImage} is: a `media:{scope}/{id}` reference or a URL, a
|
|
310
|
-
* media link included. Raw here for the reason every image field in this
|
|
309
|
+
* {@link coverImage} is: a `media:{scope}/{id}` reference or a URL, a hosted
|
|
310
|
+
* player's media link included (`video-embed-provider.ts`). Raw here for the reason every image field in this
|
|
311
311
|
* file is raw — the resolver needs the rendering host, which this record
|
|
312
312
|
* does not carry.
|
|
313
313
|
*
|