@aglyn/aglyn 1.0.0-beta.217 → 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 +14 -14
- 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/admin-audit-index.js +2 -2
- package/src/lib/app-utils/admin-audit-index.js.map +1 -1
- package/src/lib/app-utils/analytics-events.d.ts +11 -2
- package/src/lib/app-utils/analytics-events.js +11 -2
- 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/binding-tokens.js +1 -1
- package/src/lib/app-utils/binding-tokens.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/lockdown.d.ts +33 -1
- package/src/lib/app-utils/lockdown.js +45 -4
- package/src/lib/app-utils/lockdown.js.map +1 -1
- package/src/lib/app-utils/margin-utilization.d.ts +4 -69
- package/src/lib/app-utils/margin-utilization.js +82 -69
- package/src/lib/app-utils/margin-utilization.js.map +1 -1
- package/src/lib/app-utils/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.d.ts +15 -0
- package/src/lib/app-utils/screen-route.js +21 -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} +5 -2
- package/src/lib/app-utils/site-theme.js.map +1 -0
- package/src/lib/app-utils/sitemap.d.ts +4 -3
- package/src/lib/app-utils/sitemap.js +9 -7
- package/src/lib/app-utils/sitemap.js.map +1 -1
- package/src/lib/app-utils/stale-build-error.d.ts +51 -0
- package/src/lib/app-utils/stale-build-error.js +100 -1
- package/src/lib/app-utils/stale-build-error.js.map +1 -1
- package/src/lib/app-utils/starter-templates.d.ts +20 -0
- package/src/lib/app-utils/starter-templates.js +49 -0
- package/src/lib/app-utils/starter-templates.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/canvas-manager/canvas-manager.js +3 -3
- package/src/lib/canvas-manager/canvas-manager.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 +18 -135
- package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
- package/src/lib/foundation/definitions/organization.types.d.ts +6 -3
- package/src/lib/foundation/definitions/organization.types.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +27 -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 +785 -14
- package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
- package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
- package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
- package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
- package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
- package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
- package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
- package/src/lib/plugin-manager/plugin-dependents.js +75 -0
- package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
- package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
- package/src/lib/plugin-manager/plugin-distribution.js +45 -0
- package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
- package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
- package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
- package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
- package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
- package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
- package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
- package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
- package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
- package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
- package/src/lib/plugin-manager/plugin-record-index.js +51 -0
- package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
- package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
- package/src/lib/plugin-manager/plugin-record-written.js +42 -0
- package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
- package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
- package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
- package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
- package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
- package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
- package/src/lib/plugin-manager/plugin-site-export.js +40 -0
- package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
- package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
- package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
- package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
- package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
- package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
- package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
- package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
- package/src/lib/plugin-manager/plugin-usage-axes.d.ts +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js +107 -0
- package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
- package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
- package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
- package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
- package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
- package/src/lib/plugin-manager/record-captured-contact.js +10 -32
- package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
- package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
- package/src/lib/plugin-manager/repeat-rows.js +108 -0
- package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
- package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
- package/src/lib/plugin-manager/submission-record-target.js +125 -0
- package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
- package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
- package/src/lib/plugin-manager/video-embed-provider.js +165 -0
- package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
- package/src/lib/types/nodes.d.ts +2 -2
- package/src/lib/types/nodes.js.map +1 -1
- package/src/lib/app-utils/campaign-attribution.js.map +0 -1
- package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
- package/src/lib/app-utils/contact-holder.d.ts +0 -68
- package/src/lib/app-utils/contact-holder.js +0 -129
- package/src/lib/app-utils/contact-holder.js.map +0 -1
- package/src/lib/app-utils/contact-merge.d.ts +0 -136
- package/src/lib/app-utils/contact-merge.js +0 -412
- package/src/lib/app-utils/contact-merge.js.map +0 -1
- package/src/lib/app-utils/crm-csv.d.ts +0 -239
- package/src/lib/app-utils/crm-csv.js +0 -467
- package/src/lib/app-utils/crm-csv.js.map +0 -1
- package/src/lib/app-utils/crm-digest.d.ts +0 -185
- package/src/lib/app-utils/crm-digest.js +0 -272
- package/src/lib/app-utils/crm-digest.js.map +0 -1
- package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
- package/src/lib/app-utils/form-dataset-binding.js +0 -74
- package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
- package/src/lib/app-utils/list-import.d.ts +0 -158
- package/src/lib/app-utils/list-import.js +0 -332
- package/src/lib/app-utils/list-import.js.map +0 -1
- package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
- package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
- package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
- package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
- package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
- package/src/lib/app-utils/marketplace-theme.js.map +0 -1
- package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
- package/src/lib/app-utils/marketplace-update-state.js +0 -132
- package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
- package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
- package/src/lib/app-utils/publisher-agreement.js +0 -321
- package/src/lib/app-utils/publisher-agreement.js.map +0 -1
- package/src/lib/app-utils/wistia-embed.d.ts +0 -104
- package/src/lib/app-utils/wistia-embed.js +0 -138
- package/src/lib/app-utils/wistia-embed.js.map +0 -1
- package/src/lib/app-utils/workflows.d.ts +0 -136
- package/src/lib/app-utils/workflows.js +0 -346
- package/src/lib/app-utils/workflows.js.map +0 -1
- package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
- package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
- package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/stale-build-error.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 TAB THAT WAS OPEN ACROSS A DEPLOY (AGL-3279).\n *\n * Every build names its chunks by content hash and the previous build's are\n * not kept, so a tab loaded before a release asks for a file the origin no\n * longer serves the moment it navigates or opens a lazy route. The browser\n * rejects the import, the boundary above catches it, and the visitor is\n * shown a crash page with a **Try again** button that cannot possibly work:\n * `reset()` re-renders the same tree, which requests the same missing chunk.\n * It reported once in the 48h to 2026-09-23, on `aglyn.com`.\n *\n * A reload fixes it completely, because the fresh document names the chunks\n * the deploy actually shipped. What matters is that it may never LOOP: a\n * genuinely broken deploy would otherwise reload every visitor forever, and\n * an error reporter's first rule — never become the outage — applies twice\n * over to something that reloads pages.\n *\n * So the recovery is RATE-BOUND, held in session storage: at most one\n * reload per tab per {@link RECOVERY_INTERVAL_MS}, and a browser that\n * refuses storage gets none at all rather than an unbounded one. A second\n * failure inside that window renders the crash page and stays there, which\n * is the honest outcome — at that point the build really is broken and\n * reloading is not the answer.\n *\n * An interval rather than a once-ever mark, because a console tab lives for\n * days and this repo ships several times a day: a once-ever mark would\n * spend the tab's only recovery on the first deploy it survived and leave\n * it stranded on the third. Long enough that no plausible loop is\n * comfortable; short enough that the next release is covered.\n *\n * A module of its own, not a function in `error-beacon.ts`, for the reason\n * `redispatch-caught-error.ts` gives: an error boundary's chunk is\n * downloaded by every visitor, and importing one name off the beacon would\n * drag its whole installer in with it.\n */\n\nconst RELOADED_KEY = 'aglyn.staleBuildReloaded'\n\n/** How long one recovery suppresses the next, in this tab. */\nexport const RECOVERY_INTERVAL_MS = 30 * 60 * 1_000\n\n/**\n * Messages the browsers spell differently for one event — the module the\n * document names is not there.\n *\n * Matched on the MESSAGE as well as the name because only webpack's own\n * failure carries `name === 'ChunkLoadError'`; a native dynamic import that\n * 404s rejects with a plain `TypeError` whose wording is the browser's.\n */\nconst STALE_BUILD_MESSAGE =\n /Loading chunk .* failed|Failed to load chunk|Loading CSS chunk .* failed|error loading dynamically imported module|Importing a module script failed|Failed to fetch dynamically imported module/i\n\n/** Was this thrown because the document names a chunk the origin dropped? */\nexport function isStaleBuildError(error: unknown): boolean {\n if (!error) return false\n const thrown = error as { name?: unknown; message?: unknown }\n if (String(thrown.name ?? '') === 'ChunkLoadError') return true\n return STALE_BUILD_MESSAGE.test(String(thrown.message ?? ''))\n}\n\n/**\n * Should this tab reload itself for a stale build — and, if so, SPEND the\n * recovery, so asking twice cannot answer yes twice.\n *\n * The decision and the reload are separated on purpose. The decision is\n * the part with the rate limit in it, and the part that must be provable;\n * the reload is one line at the call site, which the boundary writes where\n * a reader can see it happen.\n *\n * Every storage path is guarded: an accessor may throw in a private window\n * or with site data blocked, and a failure to RECOVER from an error must\n * never be a second error.\n */\nexport function shouldReloadForStaleBuild(error: unknown): boolean {\n if (typeof window === 'undefined') return false\n if (!isStaleBuildError(error)) return false\n const now = Date.now()\n try {\n const last = Number(window.sessionStorage.getItem(RELOADED_KEY) ?? '')\n // An unreadable mark is not a recent one: a stored value we cannot\n // parse must not suppress the recovery forever.\n if (Number.isFinite(last) && last > 0 && now - last < RECOVERY_INTERVAL_MS) {\n return false\n }\n window.sessionStorage.setItem(RELOADED_KEY, String(now))\n } catch {\n // No storage is no rate limit, and an unbounded reload is worse than a\n // crash page: take the crash page.\n return false\n }\n return true\n}\n"],"names":["RELOADED_KEY","RECOVERY_INTERVAL_MS","STALE_BUILD_MESSAGE","isStaleBuildError","error","thrown","String","name","test","message","shouldReloadForStaleBuild","window","now","Date","last","Number","sessionStorage","getItem","isFinite","setItem"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GAED,MAAMA,eAAe;AAErB,4DAA4D,GAC5D,OAAO,MAAMC,uBAAuB,KAAK,KAAK,KAAK;AAEnD;;;;;;;CAOC,GACD,MAAMC,sBACJ;AAEF,2EAA2E,GAC3E,OAAO,SAASC,kBAAkBC,KAAc;QAGnCC,cAC4BA;IAHvC,IAAI,CAACD,OAAO,OAAO;IACnB,MAAMC,SAASD;IACf,IAAIE,QAAOD,eAAAA,OAAOE,IAAI,YAAXF,eAAe,QAAQ,kBAAkB,OAAO;IAC3D,OAAOH,oBAAoBM,IAAI,CAACF,QAAOD,kBAAAA,OAAOI,OAAO,YAAdJ,kBAAkB;AAC3D;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASK,0BAA0BN,KAAc;IACtD,IAAI,OAAOO,WAAW,aAAa,OAAO;IAC1C,IAAI,CAACR,kBAAkBC,QAAQ,OAAO;IACtC,MAAMQ,MAAMC,KAAKD,GAAG;IACpB,IAAI;YACkBD;QAApB,MAAMG,OAAOC,QAAOJ,iCAAAA,OAAOK,cAAc,CAACC,OAAO,CAACjB,yBAA9BW,iCAA+C;QACnE,mEAAmE;QACnE,gDAAgD;QAChD,IAAII,OAAOG,QAAQ,CAACJ,SAASA,OAAO,KAAKF,MAAME,OAAOb,sBAAsB;YAC1E,OAAO;QACT;QACAU,OAAOK,cAAc,CAACG,OAAO,CAACnB,cAAcM,OAAOM;IACrD,EAAE,eAAM;QACN,uEAAuE;QACvE,mCAAmC;QACnC,OAAO;IACT;IACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/stale-build-error.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 TAB THAT WAS OPEN ACROSS A DEPLOY (AGL-3279).\n *\n * Every build names its chunks by content hash and the previous build's are\n * not kept, so a tab loaded before a release asks for a file the origin no\n * longer serves the moment it navigates or opens a lazy route. The browser\n * rejects the import, the boundary above catches it, and the visitor is\n * shown a crash page with a **Try again** button that cannot possibly work:\n * `reset()` re-renders the same tree, which requests the same missing chunk.\n * It reported once in the 48h to 2026-09-23, on `aglyn.com`.\n *\n * A reload fixes it completely, because the fresh document names the chunks\n * the deploy actually shipped. What matters is that it may never LOOP: a\n * genuinely broken deploy would otherwise reload every visitor forever, and\n * an error reporter's first rule — never become the outage — applies twice\n * over to something that reloads pages.\n *\n * So the recovery is RATE-BOUND, held in session storage: at most one\n * reload per tab per {@link RECOVERY_INTERVAL_MS}, and a browser that\n * refuses storage gets none at all rather than an unbounded one. A second\n * failure inside that window renders the crash page and stays there, which\n * is the honest outcome — at that point the build really is broken and\n * reloading is not the answer.\n *\n * An interval rather than a once-ever mark, because a console tab lives for\n * days and this repo ships several times a day: a once-ever mark would\n * spend the tab's only recovery on the first deploy it survived and leave\n * it stranded on the third. Long enough that no plausible loop is\n * comfortable; short enough that the next release is covered.\n *\n * A module of its own, not a function in `error-beacon.ts`, for the reason\n * `redispatch-caught-error.ts` gives: an error boundary's chunk is\n * downloaded by every visitor, and importing one name off the beacon would\n * drag its whole installer in with it.\n */\n\nimport { redispatchCaughtError } from './redispatch-caught-error'\n\nconst RELOADED_KEY = 'aglyn.staleBuildReloaded'\n\n/** How long one recovery suppresses the next, in this tab. */\nexport const RECOVERY_INTERVAL_MS = 30 * 60 * 1_000\n\n/**\n * Messages the browsers spell differently for one event — the module the\n * document names is not there.\n *\n * Matched on the MESSAGE as well as the name because only webpack's own\n * failure carries `name === 'ChunkLoadError'`; a native dynamic import that\n * 404s rejects with a plain `TypeError` whose wording is the browser's.\n */\nconst STALE_BUILD_MESSAGE =\n /Loading chunk .* failed|Failed to load chunk|Loading CSS chunk .* failed|error loading dynamically imported module|Importing a module script failed|Failed to fetch dynamically imported module/i\n\n/** Was this thrown because the document names a chunk the origin dropped? */\nexport function isStaleBuildError(error: unknown): boolean {\n if (!error) return false\n const thrown = error as { name?: unknown; message?: unknown }\n if (String(thrown.name ?? '') === 'ChunkLoadError') return true\n return STALE_BUILD_MESSAGE.test(String(thrown.message ?? ''))\n}\n\n/**\n * Should this tab reload itself for a stale build — and, if so, SPEND the\n * recovery, so asking twice cannot answer yes twice.\n *\n * The decision and the reload are separated on purpose. The decision is\n * the part with the rate limit in it, and the part that must be provable;\n * the reload is one line at the call site, which the boundary writes where\n * a reader can see it happen.\n *\n * Every storage path is guarded: an accessor may throw in a private window\n * or with site data blocked, and a failure to RECOVER from an error must\n * never be a second error.\n */\nexport function shouldReloadForStaleBuild(error: unknown): boolean {\n if (typeof window === 'undefined') return false\n if (!isStaleBuildError(error)) return false\n const now = Date.now()\n try {\n const last = Number(window.sessionStorage.getItem(RELOADED_KEY) ?? '')\n // An unreadable mark is not a recent one: a stored value we cannot\n // parse must not suppress the recovery forever.\n if (Number.isFinite(last) && last > 0 && now - last < RECOVERY_INTERVAL_MS) {\n return false\n }\n window.sessionStorage.setItem(RELOADED_KEY, String(now))\n } catch {\n // No storage is no rate limit, and an unbounded reload is worse than a\n // crash page: take the crash page.\n return false\n }\n return true\n}\n\n/**\n * Withdraws the reload {@link recoverStaleBuildRejection} scheduled; true\n * when there was one.\n */\nlet cancelPending: (() => void) | null = null\n\nfunction cancelPendingReload(): boolean {\n if (!cancelPending) return false\n cancelPending()\n return true\n}\n\n/** Re-load the document; a refused reload leaves the tab where it was. */\nfunction reloadDocument(): void {\n try {\n window.location.reload()\n } catch {\n // Nothing to do: the crash page is already on screen.\n }\n}\n\n/**\n * What EVERY error boundary does with what it caught: reload once for a stale\n * build, and otherwise hand the error to the beacon. `global-error.tsx`\n * included: it catches what the root layout threw, and a stale tab's root\n * layout asks for its chunks like any other segment.\n *\n * One function so that no boundary can run half of it. The ones that matter\n * most are not the root boundaries but the two below them on a published\n * site — `PageBodyBoundary` around the page body and\n * `[host]/[scheme]/error.tsx` around the page — because a site plugin's chunk\n * is loaded by the page body, so those are what a stale tab reaches. A\n * boundary there that only re-dispatched would leave an empty body, or a\n * **Try again** that cannot work, and report a `ChunkLoadError` for it.\n *\n * A reloaded error is NOT reported; the fresh document is the fix, and the\n * tab it happened in is gone. What still reaches the beacon is the failure a\n * reload did not cure — a second one inside the recovery window, or a\n * browser that refuses storage — which the beacon labels `chunk-load`.\n *\n * `reload` is a parameter only so a spec can observe it: jsdom refuses to let\n * one redefine `location.reload`.\n *\n * @returns whether the tab is reloading.\n */\nexport function recoverStaleBuildOrReport(\n error: unknown,\n reload: () => void = reloadDocument,\n): boolean {\n // A rejection already spent this tab's recovery on a reload that waits for\n // the tab to be hidden. The page has crashed now, so there is nothing left\n // on screen to protect: take that reload immediately.\n if (isStaleBuildError(error) && cancelPendingReload()) {\n reload()\n return true\n }\n if (shouldReloadForStaleBuild(error)) {\n reload()\n return true\n }\n redispatchCaughtError(error)\n return false\n}\n\n/**\n * THE SAME TAB, WHEN NO BOUNDARY SAW IT (AGL-3423).\n *\n * An `import()` that nothing awaits inside a render, a click handler's lazy\n * module or an effect's, rejects past every boundary and reaches `window` as\n * an `unhandledrejection`. The beacon labels it `chunk-load`, and until now\n * that was all: the tab stayed on the old build with one feature silently\n * dead.\n *\n * The recovery differs from a boundary's in WHEN it reloads. A boundary\n * reloads a page that has already crashed. Here the page is still up and\n * may hold work the visitor has not saved: a Besigner edit, a half-typed\n * form. So the reload waits until the tab is hidden, the moment AGL-3280's\n * cross-pool recovery also uses, and runs at once only when the tab is\n * already hidden. The visitor comes back to the build that is deployed.\n *\n * It spends the same rate-bound mark as a boundary, so it can never loop,\n * and it absorbs every later stale-build rejection in the tab while the\n * reload is pending: they are the same stale document, not new failures.\n * A boundary that catches one meanwhile takes the pending reload at once.\n *\n * @returns whether the rejection was recovered, and so should not be\n * reported. False for any other rejection, and for a stale build whose\n * recovery is spent, which the beacon still reports as `chunk-load`.\n */\nexport function recoverStaleBuildRejection(\n reason: unknown,\n reload: () => void = reloadDocument,\n): boolean {\n if (typeof document === 'undefined' || !isStaleBuildError(reason)) return false\n if (cancelPending) return true\n if (!shouldReloadForStaleBuild(reason)) return false\n if (document.visibilityState === 'hidden') {\n reload()\n return true\n }\n const onVisibilityChange = () => {\n if (document.visibilityState !== 'hidden') return\n cancelPendingReload()\n reload()\n }\n cancelPending = () => {\n document.removeEventListener('visibilitychange', onVisibilityChange)\n cancelPending = null\n }\n document.addEventListener('visibilitychange', onVisibilityChange)\n return true\n}\n"],"names":["redispatchCaughtError","RELOADED_KEY","RECOVERY_INTERVAL_MS","STALE_BUILD_MESSAGE","isStaleBuildError","error","thrown","String","name","test","message","shouldReloadForStaleBuild","window","now","Date","last","Number","sessionStorage","getItem","isFinite","setItem","cancelPending","cancelPendingReload","reloadDocument","location","reload","recoverStaleBuildOrReport","recoverStaleBuildRejection","reason","document","visibilityState","onVisibilityChange","removeEventListener","addEventListener"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GAED,SAASA,qBAAqB,QAAQ,+BAA2B;AAEjE,MAAMC,eAAe;AAErB,4DAA4D,GAC5D,OAAO,MAAMC,uBAAuB,KAAK,KAAK,KAAK;AAEnD;;;;;;;CAOC,GACD,MAAMC,sBACJ;AAEF,2EAA2E,GAC3E,OAAO,SAASC,kBAAkBC,KAAc;QAGnCC,cAC4BA;IAHvC,IAAI,CAACD,OAAO,OAAO;IACnB,MAAMC,SAASD;IACf,IAAIE,QAAOD,eAAAA,OAAOE,IAAI,YAAXF,eAAe,QAAQ,kBAAkB,OAAO;IAC3D,OAAOH,oBAAoBM,IAAI,CAACF,QAAOD,kBAAAA,OAAOI,OAAO,YAAdJ,kBAAkB;AAC3D;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASK,0BAA0BN,KAAc;IACtD,IAAI,OAAOO,WAAW,aAAa,OAAO;IAC1C,IAAI,CAACR,kBAAkBC,QAAQ,OAAO;IACtC,MAAMQ,MAAMC,KAAKD,GAAG;IACpB,IAAI;YACkBD;QAApB,MAAMG,OAAOC,QAAOJ,iCAAAA,OAAOK,cAAc,CAACC,OAAO,CAACjB,yBAA9BW,iCAA+C;QACnE,mEAAmE;QACnE,gDAAgD;QAChD,IAAII,OAAOG,QAAQ,CAACJ,SAASA,OAAO,KAAKF,MAAME,OAAOb,sBAAsB;YAC1E,OAAO;QACT;QACAU,OAAOK,cAAc,CAACG,OAAO,CAACnB,cAAcM,OAAOM;IACrD,EAAE,eAAM;QACN,uEAAuE;QACvE,mCAAmC;QACnC,OAAO;IACT;IACA,OAAO;AACT;AAEA;;;CAGC,GACD,IAAIQ,gBAAqC;AAEzC,SAASC;IACP,IAAI,CAACD,eAAe,OAAO;IAC3BA;IACA,OAAO;AACT;AAEA,wEAAwE,GACxE,SAASE;IACP,IAAI;QACFX,OAAOY,QAAQ,CAACC,MAAM;IACxB,EAAE,eAAM;IACN,sDAAsD;IACxD;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GACD,OAAO,SAASC,0BACdrB,KAAc,EACdoB,SAAqBF,cAAc;IAEnC,2EAA2E;IAC3E,2EAA2E;IAC3E,sDAAsD;IACtD,IAAInB,kBAAkBC,UAAUiB,uBAAuB;QACrDG;QACA,OAAO;IACT;IACA,IAAId,0BAA0BN,QAAQ;QACpCoB;QACA,OAAO;IACT;IACAzB,sBAAsBK;IACtB,OAAO;AACT;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASsB,2BACdC,MAAe,EACfH,SAAqBF,cAAc;IAEnC,IAAI,OAAOM,aAAa,eAAe,CAACzB,kBAAkBwB,SAAS,OAAO;IAC1E,IAAIP,eAAe,OAAO;IAC1B,IAAI,CAACV,0BAA0BiB,SAAS,OAAO;IAC/C,IAAIC,SAASC,eAAe,KAAK,UAAU;QACzCL;QACA,OAAO;IACT;IACA,MAAMM,qBAAqB;QACzB,IAAIF,SAASC,eAAe,KAAK,UAAU;QAC3CR;QACAG;IACF;IACAJ,gBAAgB;QACdQ,SAASG,mBAAmB,CAAC,oBAAoBD;QACjDV,gBAAgB;IAClB;IACAQ,SAASI,gBAAgB,CAAC,oBAAoBF;IAC9C,OAAO;AACT"}
|
|
@@ -96,4 +96,24 @@ export declare function starterTemplateDocId(starterId: string, screenKey: strin
|
|
|
96
96
|
export declare function buildStarterTemplateDocs(starter: StarterTemplate): StarterTemplateDoc[];
|
|
97
97
|
/** Every document a starter COULD materialize as, in bundle order. */
|
|
98
98
|
export declare function buildAllStarterTemplateDocs(): StarterTemplateDoc[];
|
|
99
|
+
/**
|
|
100
|
+
* The home page every new site is born with (AGL-3408).
|
|
101
|
+
*
|
|
102
|
+
* A site used to be created with an empty routing map, so the address the
|
|
103
|
+
* create dialog calls live answered with the tenant's 404 until somebody
|
|
104
|
+
* built and published a page. `claimHostForOrg` now writes this screen, its
|
|
105
|
+
* first version and its `/` route in the same transaction that creates the
|
|
106
|
+
* host, which makes "a site with no home page" a state no new site starts in.
|
|
107
|
+
*
|
|
108
|
+
* It is PUBLIC from the first request, so the copy speaks to a visitor, never
|
|
109
|
+
* to the owner — no "edit this in the besigner". The site's name is written in
|
|
110
|
+
* as text, not bound: there is no page-content token for it, and a heading
|
|
111
|
+
* that followed a rename is not worth one.
|
|
112
|
+
*
|
|
113
|
+
* Deliberately not a starter: it is not offered in the gallery, and applying a
|
|
114
|
+
* starter may take the root back from it (see `defaultHomeScreenId`).
|
|
115
|
+
*
|
|
116
|
+
* PERSISTED IDENTIFIERS: every node id below is stored on every new site.
|
|
117
|
+
*/
|
|
118
|
+
export declare function buildDefaultHomeScreen(siteName: string): StarterTemplateScreen;
|
|
99
119
|
export declare const STARTER_TEMPLATES: StarterTemplate[];
|
|
@@ -350,6 +350,55 @@ const commerceBlock = (id, componentId, props)=>({
|
|
|
350
350
|
}
|
|
351
351
|
];
|
|
352
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* The home page every new site is born with (AGL-3408).
|
|
355
|
+
*
|
|
356
|
+
* A site used to be created with an empty routing map, so the address the
|
|
357
|
+
* create dialog calls live answered with the tenant's 404 until somebody
|
|
358
|
+
* built and published a page. `claimHostForOrg` now writes this screen, its
|
|
359
|
+
* first version and its `/` route in the same transaction that creates the
|
|
360
|
+
* host, which makes "a site with no home page" a state no new site starts in.
|
|
361
|
+
*
|
|
362
|
+
* It is PUBLIC from the first request, so the copy speaks to a visitor, never
|
|
363
|
+
* to the owner — no "edit this in the besigner". The site's name is written in
|
|
364
|
+
* as text, not bound: there is no page-content token for it, and a heading
|
|
365
|
+
* that followed a rename is not worth one.
|
|
366
|
+
*
|
|
367
|
+
* Deliberately not a starter: it is not offered in the gallery, and applying a
|
|
368
|
+
* starter may take the root back from it (see `defaultHomeScreenId`).
|
|
369
|
+
*
|
|
370
|
+
* PERSISTED IDENTIFIERS: every node id below is stored on every new site.
|
|
371
|
+
*/ export function buildDefaultHomeScreen(siteName) {
|
|
372
|
+
const name = siteName.trim() || 'Welcome';
|
|
373
|
+
return {
|
|
374
|
+
key: 'home',
|
|
375
|
+
displayName: 'Home',
|
|
376
|
+
slug: SCREEN_ROOT_PATH,
|
|
377
|
+
nodes: buildNodes([
|
|
378
|
+
section('dh_heroSection', 'md', 12, [
|
|
379
|
+
{
|
|
380
|
+
id: 'dh_hero',
|
|
381
|
+
componentId: 'muiStack',
|
|
382
|
+
props: {
|
|
383
|
+
spacing: 2
|
|
384
|
+
},
|
|
385
|
+
sx: {
|
|
386
|
+
alignItems: 'center'
|
|
387
|
+
},
|
|
388
|
+
children: [
|
|
389
|
+
text('dh_heroTitle', 'h2', name, {
|
|
390
|
+
align: 'center'
|
|
391
|
+
}),
|
|
392
|
+
text('dh_heroSub', 'h6', 'Welcome — our new website is on its way.', {
|
|
393
|
+
align: 'center',
|
|
394
|
+
color: 'text.secondary'
|
|
395
|
+
})
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
])
|
|
399
|
+
])
|
|
400
|
+
};
|
|
401
|
+
}
|
|
353
402
|
export const STARTER_TEMPLATES = [
|
|
354
403
|
{
|
|
355
404
|
id: 'landing',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/starter-templates.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// Leaf imports only, deliberately (AGL-687). This module is imported from\n// BOTH graphs — the console's client gallery renders virtual starters from\n// it, the console's seed route materializes them, and the AI plugin's server\n// shows them to a model — and neither barrel works in both: the full\n// `@aglyn/aglyn` barrel breaks the RSC/server-route graph, while the server\n// entry drags `node:fs` into the client bundle. The canvas constants are a\n// leaf with no imports of their own, and `screen-route` imports only a TYPE\n// from foundation, so both are safe on either side.\nimport { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\nimport { normalizeScreenSlug, SCREEN_ROOT_PATH } from './screen-route'\n\n/**\n * First-party starter definitions (AGL-78/79), SEED INPUT (AGL-687).\n *\n * These used to be rendered straight into the gallery and instantiated from\n * code, which made a starter a second kind of template: no version history,\n * no placeholders, no editor, changeable only by shipping a release. Now\n * they are seeded into each host's own template library as ordinary\n * `hosts/{hostId}/templates` documents (see the console's\n * `utils/server/seed-starter-templates.ts` for why copy-on-use rather than a\n * global read-only collection), and nothing reads this file at render time.\n *\n * They are also the page examples the AI plugin's template generator shows a\n * model (AGL-2909), which is why they live in the core package rather than\n * beside the console gallery that renders them.\n *\n * Node ids stay template-local (unique within each screen's version doc);\n * `createPageFromTemplate` re-keys screen and version ids at instantiation.\n *\n * PERSISTED IDENTIFIERS: `StarterTemplate.id`, `StarterTemplateScreen.key`\n * and every node id below appear in stored documents (seeded template doc\n * ids are derived from the first two). They must never be renamed.\n */\nexport interface StarterTemplateScreen {\n /**\n * Stable, starter-local key. Part of the seeded document id, so it is a\n * persisted identifier — never rename one.\n */\n key: string\n displayName: string\n description?: string\n /**\n * Routing-map slug. `SCREEN_ROOT_PATH` (`'/'`) asks for the site root — the\n * home page — and anything else is a single path segment.\n *\n * It used to be `''` that meant home, which no normalizer agreed with: both\n * the apply path and the seed below read an empty string as \"no address\n * authored\" and derived one from the display name, so the shop starters'\n * home page was published at `/home` and the site 404'd at its own URL\n * (AGL-1575).\n */\n slug: string\n seo?: { title?: string; description?: string }\n /** Flat node map including the canvas root. */\n nodes: Record<string, any>\n}\n\nexport interface StarterTemplate {\n id: string\n displayName: string\n description: string\n category: string\n screens: StarterTemplateScreen[]\n}\n\n/** A materialized starter template document, keyed by its deterministic id. */\nexport interface StarterTemplateDoc {\n id: string\n /** Which starter this page belongs to — lets one starter be selected. */\n starterId: string\n data: Record<string, unknown>\n}\n\n/**\n * Deterministic document id for a seeded starter screen (AGL-687).\n *\n * Derived from the starter id and the screen key — never randomly generated\n * — so re-running the seed addresses the same documents instead of stacking\n * duplicate copies of every starter on every run.\n */\nexport function starterTemplateDocId(\n starterId: string,\n screenKey: string,\n): string {\n return `starter-${starterId}-${screenKey}`\n}\n\n/**\n * Expands one starter into the template documents it seeds as.\n *\n * One page template per screen, matching what a marketplace install of a\n * multi-screen site template already produces (AGL-669) — the alternative,\n * a bespoke multi-page template shape, would be a third lifecycle on top of\n * the two this issue exists to merge. The bundle's name/description/order\n * ride `source` so the gallery can still present the starter as one card.\n */\nexport function buildStarterTemplateDocs(\n starter: StarterTemplate,\n): StarterTemplateDoc[] {\n return starter.screens.map((screen, index) => {\n // Normalized on the way into the document so the persisted address is in\n // the routing-map format the tenant matches against — in particular the\n // root, which the previous `screen.slug ? …` guard dropped entirely when\n // home was spelled `''` (AGL-1575). A slug that sanitizes away carries no\n // field, and the apply path derives one from the display name.\n const slug = normalizeScreenSlug(screen.slug)\n return {\n id: starterTemplateDocId(starter.id, screen.key),\n starterId: starter.id,\n data: {\n kind: 'page',\n displayName: screen.displayName,\n // Only the screen's own description, never the starter blurb: a\n // template's `description` is carried onto the page it creates, and a\n // screen description is the live site's meta-description fallback.\n // The bundle blurb lives on `source.starterDescription` instead.\n ...(screen.description ? { description: screen.description } : {}),\n category: starter.category,\n ...(slug ? { slug } : {}),\n ...(screen.seo ? { seo: screen.seo } : {}),\n nodes: screen.nodes,\n source: {\n type: 'starter',\n starterId: starter.id,\n starterName: starter.displayName,\n starterDescription: starter.description,\n starterOrder: index,\n },\n },\n }\n })\n}\n\n/** Every document a starter COULD materialize as, in bundle order. */\nexport function buildAllStarterTemplateDocs(): StarterTemplateDoc[] {\n return STARTER_TEMPLATES.flatMap(buildStarterTemplateDocs)\n}\n\ntype NodeSpec = {\n id: string\n componentId: string\n /** Bundle owning the component; defaults to 'mui' (AGL-300). */\n pluginId?: string\n props?: Record<string, unknown>\n /**\n * Node-level styles — a SIBLING of props, never `props.sx` (AGL-1346).\n *\n * Both records render (`Leaf` composes `(sx, props.sx, node.sx)`, later\n * wins), but the Styles panel edits `node.sx`. A starter that seeded its\n * styling into `props.sx` handed the author a document full of values\n * the panel could show but no click could change or clear.\n */\n sx?: Record<string, unknown>\n children?: NodeSpec[]\n}\n\n/** Builds the flat, persisted node map from a nested spec. */\nfunction buildNodes(children: NodeSpec[]): Record<string, any> {\n const map: Record<string, any> = {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n nodes: children.map((child) => child.id),\n },\n }\n const walk = (spec: NodeSpec, parentId: string) => {\n map[spec.id] = {\n $id: spec.id,\n componentId: spec.componentId,\n pluginId: spec.pluginId ?? 'mui',\n parentId,\n props: spec.props ?? {},\n ...(spec.sx ? { sx: spec.sx } : {}),\n nodes: (spec.children ?? []).map((child) => child.id),\n }\n for (const child of spec.children ?? []) walk(child, spec.id)\n }\n for (const child of children) walk(child, CANVAS_ROOT_ELEMENT_ID)\n return map\n}\n\nconst text = (\n id: string,\n variant: string,\n children: string,\n extra?: Record<string, unknown>,\n): NodeSpec => ({\n id,\n componentId: 'muiTypography',\n props: { variant, children, ...extra },\n})\n\n/**\n * The stock widths a starter band may be (AGL-1932, AGL-1298).\n *\n * Three cases, and only three, because the standard has three. There is no\n * pixel case: `1328` is a CONTENT width, not a breakpoint, and the ban on\n * bespoke numbers is the half of AGL-1298 these templates used to violate\n * four times over.\n */\ntype SectionWidth = 'md' | 'lg' | 'xl'\n\n/**\n * One page band: a `Container` at a stock width, carrying the band's own\n * vertical rhythm (AGL-1932).\n *\n * The layout standard (AGL-1298): every section is a Container. Full-bleed is\n * the Container's own full-width attribute, never the absence of a Container,\n * and anything else is one of three stock widths. A template that seeds no\n * Container hands every customer a site that starts outside the standard.\n *\n * How the three widths are chosen here, stated so the next audit does not\n * have to guess:\n *\n * - `xl` — a marketing band: hero, feature row, product grid, gallery. The\n * default width, and what an unremarkable page should be.\n * - `lg` — the deliberate middle case: wide but text-led and interactive.\n * Cart and account are the honest instances, not decoration.\n * - `md` — long-form prose and narrow form columns, on READING grounds: at\n * `xl` a paragraph runs 110–120 characters a line. This is the case the\n * `Prose Container` preset exists for.\n *\n * Horizontal padding is the Container's own gutters, so bands no longer carry\n * horizontal padding at all. Only the vertical rhythm rides here, which is\n * rhythm rather than width.\n *\n * Written as the two longhands rather than MUI's `py` (AGL-2207/2208): the\n * Styles panel's Padding control is named for the four sides, so a band\n * seeded with `py` handed the customer a document whose padding the panel\n * showed as empty and no click could clear.\n */\nconst section = (\n id: string,\n maxWidth: SectionWidth,\n verticalPadding: number,\n children: NodeSpec[],\n): NodeSpec => ({\n id,\n componentId: 'muiContainer',\n props: { maxWidth },\n sx: { paddingTop: verticalPadding, paddingBottom: verticalPadding },\n children,\n})\n\nconst heroSection = (prefix: string, headline: string, tagline: string) =>\n // The Container is a NEW node (`…heroSection`); `…hero` keeps its id and\n // stays the Stack. Node ids here are persisted identifiers, so bands are\n // wrapped rather than re-pointed.\n section(`${prefix}heroSection`, 'xl', 10, [\n {\n id: `${prefix}hero`,\n componentId: 'muiStack',\n props: { spacing: 2 },\n sx: { alignItems: 'center' },\n children: [\n text(`${prefix}heroTitle`, 'h2', headline, { align: 'center' }),\n text(`${prefix}heroSub`, 'h6', tagline, { align: 'center' }),\n {\n id: `${prefix}heroCta`,\n componentId: 'muiButton',\n props: {\n variant: 'contained',\n size: 'large',\n children: 'Get in touch',\n },\n },\n ],\n },\n ])\n\nconst featureColumn = (id: string, title: string, body: string): NodeSpec => ({\n id,\n componentId: 'muiStack',\n props: { spacing: 1 },\n // `flex: 1` is a column's share of its row, not a width cap — it survives\n // the sweep deliberately. Only `maxWidth` in raw pixels was the violation.\n sx: {\n flex: 1,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n children: [text(`${id}T`, 'h5', title), text(`${id}B`, 'body1', body)],\n})\n\nconst contactForm = (prefix: string): NodeSpec => ({\n id: `${prefix}form`,\n componentId: 'form',\n props: {\n formName: 'Contact',\n submitLabel: 'Send message',\n successMessage: 'Thanks — we will get back to you soon.',\n },\n children: [\n {\n id: `${prefix}fName`,\n componentId: 'formField',\n props: { fieldName: 'name', label: 'Name', required: true },\n },\n {\n id: `${prefix}fEmail`,\n componentId: 'formField',\n props: {\n fieldName: 'email',\n label: 'Email',\n fieldType: 'email',\n required: true,\n },\n },\n {\n id: `${prefix}fMessage`,\n componentId: 'formField',\n props: {\n fieldName: 'message',\n label: 'Message',\n fieldType: 'textarea',\n required: true,\n },\n },\n ],\n})\n\n\nconst commerceBlock = (\n id: string,\n componentId: string,\n props?: Record<string, unknown>,\n): NodeSpec => ({ id, componentId, pluginId: 'commerce', props })\n\n/** Shared screens for the shop starters (AGL-300). */\nfunction shopScreens(prefix: string, digital: boolean): StarterTemplateScreen[] {\n return [\n {\n key: 'home',\n displayName: 'Home',\n // The site root, spelled the way the routing map spells it. `''` here\n // read as \"no address\" everywhere downstream and put the shop's home\n // page at `/home` (AGL-1575).\n slug: SCREEN_ROOT_PATH,\n seo: {\n title: digital ? 'Digital shop' : 'Shop',\n description: 'Browse our products.',\n },\n nodes: buildNodes([\n heroSection(\n `${prefix}h_`,\n digital ? 'Downloads that level you up' : 'Gear you can trust',\n digital\n ? 'Instant delivery. Lifetime updates.'\n : 'Quality parts, shipped fast.',\n ),\n section(`${prefix}h_gridSection`, 'xl', 6, [\n commerceBlock(`${prefix}h_grid`, 'product-grid', {\n source: 'all',\n sort: 'newest',\n columns: 3,\n maxItems: 6,\n }),\n ]),\n // A newsletter capture is a short text-led band, not a gallery.\n section(`${prefix}h_newsSection`, 'md', 6, [\n commerceBlock(`${prefix}h_news`, 'newsletter-signup', {\n heading: 'Get updates and offers',\n }),\n ]),\n ]),\n },\n {\n key: 'shop',\n displayName: 'Shop',\n slug: 'shop',\n seo: { title: 'All products' },\n nodes: buildNodes([\n section(`${prefix}s_section`, 'xl', 6, [\n text(`${prefix}s_title`, 'h3', 'All products'),\n commerceBlock(`${prefix}s_grid`, 'product-grid', {\n source: 'all',\n columns: 4,\n showFilters: true,\n }),\n ]),\n ]),\n },\n {\n key: 'product',\n displayName: 'Product page',\n slug: 'product',\n seo: { title: 'Product' },\n nodes: buildNodes([\n section(`${prefix}p_section`, 'xl', 6, [\n commerceBlock(`${prefix}p_detail`, 'product-detail', {}),\n ]),\n ]),\n },\n {\n key: 'cart',\n displayName: 'Cart',\n slug: 'cart',\n seo: { title: 'Your cart' },\n // LG, the deliberate middle case: a cart is a wide table but it is read\n // line by line, so a full XL band spreads it further than the eye\n // tracks. Same for the account screen below.\n nodes: buildNodes([\n section(`${prefix}c_section`, 'lg', 6, [\n text(`${prefix}c_title`, 'h3', 'Your cart'),\n commerceBlock(`${prefix}c_cart`, 'cart', {\n variant: 'inline',\n showCoupon: true,\n }),\n ]),\n ]),\n },\n {\n key: 'account',\n displayName: 'Account',\n slug: 'account',\n seo: { title: 'Your account' },\n nodes: buildNodes([\n section(`${prefix}a_section`, 'lg', 6, [\n commerceBlock(`${prefix}a_account`, 'customer-account', {\n signedOutHeading: 'Your account',\n }),\n ]),\n ]),\n },\n ]\n}\n\nexport const STARTER_TEMPLATES: StarterTemplate[] = [\n {\n id: 'landing',\n displayName: 'Landing Page',\n description:\n 'One-page launch site: hero, three selling points, and a contact form.',\n category: 'Marketing',\n screens: [\n {\n key: 'landing',\n displayName: 'Landing',\n slug: 'landing',\n seo: {\n title: 'Welcome',\n description: 'Everything you need to know, on one page.',\n },\n nodes: buildNodes([\n heroSection(\n 'l_',\n 'A headline that sells your idea',\n 'One clear sentence about the value you deliver.',\n ),\n section('l_featuresSection', 'xl', 6, [\n {\n id: 'l_features',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn(\n 'l_f1',\n 'Fast',\n 'Explain the first reason customers pick you.',\n ),\n featureColumn(\n 'l_f2',\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'l_f3',\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n },\n ]),\n // Was `maxWidth: 560`. A band, not an inner measure: it sat at the\n // top level carrying the section's own gutters and rhythm, so the\n // pixel value was capping the BAND. MD is the stock width for a\n // narrow, form-led column.\n section('l_contactSection', 'md', 6, [\n {\n id: 'l_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('l_contactTitle', 'h4', 'Get in touch'),\n contactForm('l_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'business',\n displayName: 'Business',\n description:\n 'Company site: home with services, an about page, and a contact page.',\n category: 'Business',\n screens: [\n {\n key: 'home',\n displayName: 'Business Home',\n slug: 'home',\n seo: { title: 'Home' },\n nodes: buildNodes([\n heroSection(\n 'b_',\n 'Your business, done right',\n 'Tell visitors what you do and who you do it for.',\n ),\n section('b_servicesSection', 'xl', 6, [\n {\n id: 'b_services',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn('b_s1', 'Service one', 'Describe this service.'),\n featureColumn('b_s2', 'Service two', 'Describe this service.'),\n featureColumn(\n 'b_s3',\n 'Service three',\n 'Describe this service.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'about-us',\n displayName: 'About Us',\n slug: 'about-us',\n seo: { title: 'About us' },\n nodes: buildNodes([\n // Was `maxWidth: 720` — the one unambiguous PROSE band in the set,\n // and the case the Prose Container preset exists for. MD (900px)\n // lands near the 65–75 characters a line that reads comfortably.\n section('a_wrapSection', 'md', 8, [\n {\n id: 'a_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('a_title', 'h3', 'About us'),\n text(\n 'a_body',\n 'body1',\n 'Share your story: how you started, what you believe, and ' +\n 'why customers trust you.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'contact-us',\n displayName: 'Contact Us',\n slug: 'contact-us',\n seo: { title: 'Contact' },\n nodes: buildNodes([\n // Was `maxWidth: 560`. Same reading as the landing page's contact\n // band: text plus a form column, so MD.\n section('c_wrapSection', 'md', 8, [\n {\n id: 'c_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('c_title', 'h3', 'Contact us'),\n text(\n 'c_body',\n 'body1',\n 'Questions or quotes — send a message and we reply within ' +\n 'one business day.',\n ),\n contactForm('c_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'portfolio',\n displayName: 'Portfolio',\n description: 'Personal portfolio: intro, work grid, and contact form.',\n category: 'Personal',\n screens: [\n {\n key: 'portfolio',\n displayName: 'Portfolio',\n slug: 'portfolio',\n seo: { title: 'Portfolio' },\n nodes: buildNodes([\n heroSection(\n 'p_',\n 'Hi, I make things',\n 'Designer / developer / photographer — introduce yourself here.',\n ),\n section('p_gridSection', 'xl', 6, [\n {\n id: 'p_grid',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n {\n id: 'p_img1',\n componentId: 'image',\n // `height` is an image's own intrinsic sizing prop, not a\n // band cap — out of scope for the container standard.\n props: { alt: 'Project one', height: '220px' },\n },\n {\n id: 'p_img2',\n componentId: 'image',\n props: { alt: 'Project two', height: '220px' },\n },\n {\n id: 'p_img3',\n componentId: 'image',\n props: { alt: 'Project three', height: '220px' },\n },\n ],\n },\n ]),\n // Was `maxWidth: 560`, the third of the three contact bands.\n section('p_contactSection', 'md', 6, [\n {\n id: 'p_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('p_contactTitle', 'h4', 'Work with me'),\n contactForm('p_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'physical-shop',\n displayName: 'Shop (physical products)',\n description:\n 'Storefront starter: home with featured products, filterable shop, ' +\n 'product page, cart, and customer accounts. After applying, set the ' +\n 'Product page as the product template in Store settings.',\n category: 'Commerce',\n screens: shopScreens('ps_', false),\n },\n {\n id: 'digital-shop',\n displayName: 'Shop (digital products)',\n description:\n 'Digital storefront starter: downloads-focused home, shop, product ' +\n 'page, cart, and accounts with a newsletter capture. Set the Product ' +\n 'page as the product template in Store settings after applying.',\n category: 'Commerce',\n screens: shopScreens('ds_', true),\n },\n]\n"],"names":["CANVAS_ROOT_ELEMENT_ID","normalizeScreenSlug","SCREEN_ROOT_PATH","starterTemplateDocId","starterId","screenKey","buildStarterTemplateDocs","starter","screens","map","screen","index","slug","id","key","data","kind","displayName","description","category","seo","nodes","source","type","starterName","starterDescription","starterOrder","buildAllStarterTemplateDocs","STARTER_TEMPLATES","flatMap","buildNodes","children","$id","componentId","child","walk","spec","parentId","pluginId","props","sx","text","variant","extra","section","maxWidth","verticalPadding","paddingTop","paddingBottom","heroSection","prefix","headline","tagline","spacing","alignItems","align","size","featureColumn","title","body","flex","paddingRight","paddingLeft","contactForm","formName","submitLabel","successMessage","fieldName","label","required","fieldType","commerceBlock","shopScreens","digital","sort","columns","maxItems","heading","showFilters","showCoupon","signedOutHeading","direction","alt","height"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/starter-templates.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// Leaf imports only, deliberately (AGL-687). This module is imported from\n// BOTH graphs — the console's client gallery renders virtual starters from\n// it, the console's seed route materializes them, and the AI plugin's server\n// shows them to a model — and neither barrel works in both: the full\n// `@aglyn/aglyn` barrel breaks the RSC/server-route graph, while the server\n// entry drags `node:fs` into the client bundle. The canvas constants are a\n// leaf with no imports of their own, and `screen-route` imports only a TYPE\n// from foundation, so both are safe on either side.\nimport { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\nimport { normalizeScreenSlug, SCREEN_ROOT_PATH } from './screen-route'\n\n/**\n * First-party starter definitions (AGL-78/79), SEED INPUT (AGL-687).\n *\n * These used to be rendered straight into the gallery and instantiated from\n * code, which made a starter a second kind of template: no version history,\n * no placeholders, no editor, changeable only by shipping a release. Now\n * they are seeded into each host's own template library as ordinary\n * `hosts/{hostId}/templates` documents (see the console's\n * `utils/server/seed-starter-templates.ts` for why copy-on-use rather than a\n * global read-only collection), and nothing reads this file at render time.\n *\n * They are also the page examples the AI plugin's template generator shows a\n * model (AGL-2909), which is why they live in the core package rather than\n * beside the console gallery that renders them.\n *\n * Node ids stay template-local (unique within each screen's version doc);\n * `createPageFromTemplate` re-keys screen and version ids at instantiation.\n *\n * PERSISTED IDENTIFIERS: `StarterTemplate.id`, `StarterTemplateScreen.key`\n * and every node id below appear in stored documents (seeded template doc\n * ids are derived from the first two). They must never be renamed.\n */\nexport interface StarterTemplateScreen {\n /**\n * Stable, starter-local key. Part of the seeded document id, so it is a\n * persisted identifier — never rename one.\n */\n key: string\n displayName: string\n description?: string\n /**\n * Routing-map slug. `SCREEN_ROOT_PATH` (`'/'`) asks for the site root — the\n * home page — and anything else is a single path segment.\n *\n * It used to be `''` that meant home, which no normalizer agreed with: both\n * the apply path and the seed below read an empty string as \"no address\n * authored\" and derived one from the display name, so the shop starters'\n * home page was published at `/home` and the site 404'd at its own URL\n * (AGL-1575).\n */\n slug: string\n seo?: { title?: string; description?: string }\n /** Flat node map including the canvas root. */\n nodes: Record<string, any>\n}\n\nexport interface StarterTemplate {\n id: string\n displayName: string\n description: string\n category: string\n screens: StarterTemplateScreen[]\n}\n\n/** A materialized starter template document, keyed by its deterministic id. */\nexport interface StarterTemplateDoc {\n id: string\n /** Which starter this page belongs to — lets one starter be selected. */\n starterId: string\n data: Record<string, unknown>\n}\n\n/**\n * Deterministic document id for a seeded starter screen (AGL-687).\n *\n * Derived from the starter id and the screen key — never randomly generated\n * — so re-running the seed addresses the same documents instead of stacking\n * duplicate copies of every starter on every run.\n */\nexport function starterTemplateDocId(\n starterId: string,\n screenKey: string,\n): string {\n return `starter-${starterId}-${screenKey}`\n}\n\n/**\n * Expands one starter into the template documents it seeds as.\n *\n * One page template per screen, matching what a marketplace install of a\n * multi-screen site template already produces (AGL-669) — the alternative,\n * a bespoke multi-page template shape, would be a third lifecycle on top of\n * the two this issue exists to merge. The bundle's name/description/order\n * ride `source` so the gallery can still present the starter as one card.\n */\nexport function buildStarterTemplateDocs(\n starter: StarterTemplate,\n): StarterTemplateDoc[] {\n return starter.screens.map((screen, index) => {\n // Normalized on the way into the document so the persisted address is in\n // the routing-map format the tenant matches against — in particular the\n // root, which the previous `screen.slug ? …` guard dropped entirely when\n // home was spelled `''` (AGL-1575). A slug that sanitizes away carries no\n // field, and the apply path derives one from the display name.\n const slug = normalizeScreenSlug(screen.slug)\n return {\n id: starterTemplateDocId(starter.id, screen.key),\n starterId: starter.id,\n data: {\n kind: 'page',\n displayName: screen.displayName,\n // Only the screen's own description, never the starter blurb: a\n // template's `description` is carried onto the page it creates, and a\n // screen description is the live site's meta-description fallback.\n // The bundle blurb lives on `source.starterDescription` instead.\n ...(screen.description ? { description: screen.description } : {}),\n category: starter.category,\n ...(slug ? { slug } : {}),\n ...(screen.seo ? { seo: screen.seo } : {}),\n nodes: screen.nodes,\n source: {\n type: 'starter',\n starterId: starter.id,\n starterName: starter.displayName,\n starterDescription: starter.description,\n starterOrder: index,\n },\n },\n }\n })\n}\n\n/** Every document a starter COULD materialize as, in bundle order. */\nexport function buildAllStarterTemplateDocs(): StarterTemplateDoc[] {\n return STARTER_TEMPLATES.flatMap(buildStarterTemplateDocs)\n}\n\ntype NodeSpec = {\n id: string\n componentId: string\n /** Bundle owning the component; defaults to 'mui' (AGL-300). */\n pluginId?: string\n props?: Record<string, unknown>\n /**\n * Node-level styles — a SIBLING of props, never `props.sx` (AGL-1346).\n *\n * Both records render (`Leaf` composes `(sx, props.sx, node.sx)`, later\n * wins), but the Styles panel edits `node.sx`. A starter that seeded its\n * styling into `props.sx` handed the author a document full of values\n * the panel could show but no click could change or clear.\n */\n sx?: Record<string, unknown>\n children?: NodeSpec[]\n}\n\n/** Builds the flat, persisted node map from a nested spec. */\nfunction buildNodes(children: NodeSpec[]): Record<string, any> {\n const map: Record<string, any> = {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n nodes: children.map((child) => child.id),\n },\n }\n const walk = (spec: NodeSpec, parentId: string) => {\n map[spec.id] = {\n $id: spec.id,\n componentId: spec.componentId,\n pluginId: spec.pluginId ?? 'mui',\n parentId,\n props: spec.props ?? {},\n ...(spec.sx ? { sx: spec.sx } : {}),\n nodes: (spec.children ?? []).map((child) => child.id),\n }\n for (const child of spec.children ?? []) walk(child, spec.id)\n }\n for (const child of children) walk(child, CANVAS_ROOT_ELEMENT_ID)\n return map\n}\n\nconst text = (\n id: string,\n variant: string,\n children: string,\n extra?: Record<string, unknown>,\n): NodeSpec => ({\n id,\n componentId: 'muiTypography',\n props: { variant, children, ...extra },\n})\n\n/**\n * The stock widths a starter band may be (AGL-1932, AGL-1298).\n *\n * Three cases, and only three, because the standard has three. There is no\n * pixel case: `1328` is a CONTENT width, not a breakpoint, and the ban on\n * bespoke numbers is the half of AGL-1298 these templates used to violate\n * four times over.\n */\ntype SectionWidth = 'md' | 'lg' | 'xl'\n\n/**\n * One page band: a `Container` at a stock width, carrying the band's own\n * vertical rhythm (AGL-1932).\n *\n * The layout standard (AGL-1298): every section is a Container. Full-bleed is\n * the Container's own full-width attribute, never the absence of a Container,\n * and anything else is one of three stock widths. A template that seeds no\n * Container hands every customer a site that starts outside the standard.\n *\n * How the three widths are chosen here, stated so the next audit does not\n * have to guess:\n *\n * - `xl` — a marketing band: hero, feature row, product grid, gallery. The\n * default width, and what an unremarkable page should be.\n * - `lg` — the deliberate middle case: wide but text-led and interactive.\n * Cart and account are the honest instances, not decoration.\n * - `md` — long-form prose and narrow form columns, on READING grounds: at\n * `xl` a paragraph runs 110–120 characters a line. This is the case the\n * `Prose Container` preset exists for.\n *\n * Horizontal padding is the Container's own gutters, so bands no longer carry\n * horizontal padding at all. Only the vertical rhythm rides here, which is\n * rhythm rather than width.\n *\n * Written as the two longhands rather than MUI's `py` (AGL-2207/2208): the\n * Styles panel's Padding control is named for the four sides, so a band\n * seeded with `py` handed the customer a document whose padding the panel\n * showed as empty and no click could clear.\n */\nconst section = (\n id: string,\n maxWidth: SectionWidth,\n verticalPadding: number,\n children: NodeSpec[],\n): NodeSpec => ({\n id,\n componentId: 'muiContainer',\n props: { maxWidth },\n sx: { paddingTop: verticalPadding, paddingBottom: verticalPadding },\n children,\n})\n\nconst heroSection = (prefix: string, headline: string, tagline: string) =>\n // The Container is a NEW node (`…heroSection`); `…hero` keeps its id and\n // stays the Stack. Node ids here are persisted identifiers, so bands are\n // wrapped rather than re-pointed.\n section(`${prefix}heroSection`, 'xl', 10, [\n {\n id: `${prefix}hero`,\n componentId: 'muiStack',\n props: { spacing: 2 },\n sx: { alignItems: 'center' },\n children: [\n text(`${prefix}heroTitle`, 'h2', headline, { align: 'center' }),\n text(`${prefix}heroSub`, 'h6', tagline, { align: 'center' }),\n {\n id: `${prefix}heroCta`,\n componentId: 'muiButton',\n props: {\n variant: 'contained',\n size: 'large',\n children: 'Get in touch',\n },\n },\n ],\n },\n ])\n\nconst featureColumn = (id: string, title: string, body: string): NodeSpec => ({\n id,\n componentId: 'muiStack',\n props: { spacing: 1 },\n // `flex: 1` is a column's share of its row, not a width cap — it survives\n // the sweep deliberately. Only `maxWidth` in raw pixels was the violation.\n sx: {\n flex: 1,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n children: [text(`${id}T`, 'h5', title), text(`${id}B`, 'body1', body)],\n})\n\nconst contactForm = (prefix: string): NodeSpec => ({\n id: `${prefix}form`,\n componentId: 'form',\n props: {\n formName: 'Contact',\n submitLabel: 'Send message',\n successMessage: 'Thanks — we will get back to you soon.',\n },\n children: [\n {\n id: `${prefix}fName`,\n componentId: 'formField',\n props: { fieldName: 'name', label: 'Name', required: true },\n },\n {\n id: `${prefix}fEmail`,\n componentId: 'formField',\n props: {\n fieldName: 'email',\n label: 'Email',\n fieldType: 'email',\n required: true,\n },\n },\n {\n id: `${prefix}fMessage`,\n componentId: 'formField',\n props: {\n fieldName: 'message',\n label: 'Message',\n fieldType: 'textarea',\n required: true,\n },\n },\n ],\n})\n\n\nconst commerceBlock = (\n id: string,\n componentId: string,\n props?: Record<string, unknown>,\n): NodeSpec => ({ id, componentId, pluginId: 'commerce', props })\n\n/** Shared screens for the shop starters (AGL-300). */\nfunction shopScreens(prefix: string, digital: boolean): StarterTemplateScreen[] {\n return [\n {\n key: 'home',\n displayName: 'Home',\n // The site root, spelled the way the routing map spells it. `''` here\n // read as \"no address\" everywhere downstream and put the shop's home\n // page at `/home` (AGL-1575).\n slug: SCREEN_ROOT_PATH,\n seo: {\n title: digital ? 'Digital shop' : 'Shop',\n description: 'Browse our products.',\n },\n nodes: buildNodes([\n heroSection(\n `${prefix}h_`,\n digital ? 'Downloads that level you up' : 'Gear you can trust',\n digital\n ? 'Instant delivery. Lifetime updates.'\n : 'Quality parts, shipped fast.',\n ),\n section(`${prefix}h_gridSection`, 'xl', 6, [\n commerceBlock(`${prefix}h_grid`, 'product-grid', {\n source: 'all',\n sort: 'newest',\n columns: 3,\n maxItems: 6,\n }),\n ]),\n // A newsletter capture is a short text-led band, not a gallery.\n section(`${prefix}h_newsSection`, 'md', 6, [\n commerceBlock(`${prefix}h_news`, 'newsletter-signup', {\n heading: 'Get updates and offers',\n }),\n ]),\n ]),\n },\n {\n key: 'shop',\n displayName: 'Shop',\n slug: 'shop',\n seo: { title: 'All products' },\n nodes: buildNodes([\n section(`${prefix}s_section`, 'xl', 6, [\n text(`${prefix}s_title`, 'h3', 'All products'),\n commerceBlock(`${prefix}s_grid`, 'product-grid', {\n source: 'all',\n columns: 4,\n showFilters: true,\n }),\n ]),\n ]),\n },\n {\n key: 'product',\n displayName: 'Product page',\n slug: 'product',\n seo: { title: 'Product' },\n nodes: buildNodes([\n section(`${prefix}p_section`, 'xl', 6, [\n commerceBlock(`${prefix}p_detail`, 'product-detail', {}),\n ]),\n ]),\n },\n {\n key: 'cart',\n displayName: 'Cart',\n slug: 'cart',\n seo: { title: 'Your cart' },\n // LG, the deliberate middle case: a cart is a wide table but it is read\n // line by line, so a full XL band spreads it further than the eye\n // tracks. Same for the account screen below.\n nodes: buildNodes([\n section(`${prefix}c_section`, 'lg', 6, [\n text(`${prefix}c_title`, 'h3', 'Your cart'),\n commerceBlock(`${prefix}c_cart`, 'cart', {\n variant: 'inline',\n showCoupon: true,\n }),\n ]),\n ]),\n },\n {\n key: 'account',\n displayName: 'Account',\n slug: 'account',\n seo: { title: 'Your account' },\n nodes: buildNodes([\n section(`${prefix}a_section`, 'lg', 6, [\n commerceBlock(`${prefix}a_account`, 'customer-account', {\n signedOutHeading: 'Your account',\n }),\n ]),\n ]),\n },\n ]\n}\n\n/**\n * The home page every new site is born with (AGL-3408).\n *\n * A site used to be created with an empty routing map, so the address the\n * create dialog calls live answered with the tenant's 404 until somebody\n * built and published a page. `claimHostForOrg` now writes this screen, its\n * first version and its `/` route in the same transaction that creates the\n * host, which makes \"a site with no home page\" a state no new site starts in.\n *\n * It is PUBLIC from the first request, so the copy speaks to a visitor, never\n * to the owner — no \"edit this in the besigner\". The site's name is written in\n * as text, not bound: there is no page-content token for it, and a heading\n * that followed a rename is not worth one.\n *\n * Deliberately not a starter: it is not offered in the gallery, and applying a\n * starter may take the root back from it (see `defaultHomeScreenId`).\n *\n * PERSISTED IDENTIFIERS: every node id below is stored on every new site.\n */\nexport function buildDefaultHomeScreen(siteName: string): StarterTemplateScreen {\n const name = siteName.trim() || 'Welcome'\n return {\n key: 'home',\n displayName: 'Home',\n slug: SCREEN_ROOT_PATH,\n nodes: buildNodes([\n section('dh_heroSection', 'md', 12, [\n {\n id: 'dh_hero',\n componentId: 'muiStack',\n props: { spacing: 2 },\n sx: { alignItems: 'center' },\n children: [\n text('dh_heroTitle', 'h2', name, { align: 'center' }),\n text(\n 'dh_heroSub',\n 'h6',\n 'Welcome — our new website is on its way.',\n { align: 'center', color: 'text.secondary' },\n ),\n ],\n },\n ]),\n ]),\n }\n}\n\nexport const STARTER_TEMPLATES: StarterTemplate[] = [\n {\n id: 'landing',\n displayName: 'Landing Page',\n description:\n 'One-page launch site: hero, three selling points, and a contact form.',\n category: 'Marketing',\n screens: [\n {\n key: 'landing',\n displayName: 'Landing',\n slug: 'landing',\n seo: {\n title: 'Welcome',\n description: 'Everything you need to know, on one page.',\n },\n nodes: buildNodes([\n heroSection(\n 'l_',\n 'A headline that sells your idea',\n 'One clear sentence about the value you deliver.',\n ),\n section('l_featuresSection', 'xl', 6, [\n {\n id: 'l_features',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn(\n 'l_f1',\n 'Fast',\n 'Explain the first reason customers pick you.',\n ),\n featureColumn(\n 'l_f2',\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'l_f3',\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n },\n ]),\n // Was `maxWidth: 560`. A band, not an inner measure: it sat at the\n // top level carrying the section's own gutters and rhythm, so the\n // pixel value was capping the BAND. MD is the stock width for a\n // narrow, form-led column.\n section('l_contactSection', 'md', 6, [\n {\n id: 'l_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('l_contactTitle', 'h4', 'Get in touch'),\n contactForm('l_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'business',\n displayName: 'Business',\n description:\n 'Company site: home with services, an about page, and a contact page.',\n category: 'Business',\n screens: [\n {\n key: 'home',\n displayName: 'Business Home',\n slug: 'home',\n seo: { title: 'Home' },\n nodes: buildNodes([\n heroSection(\n 'b_',\n 'Your business, done right',\n 'Tell visitors what you do and who you do it for.',\n ),\n section('b_servicesSection', 'xl', 6, [\n {\n id: 'b_services',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn('b_s1', 'Service one', 'Describe this service.'),\n featureColumn('b_s2', 'Service two', 'Describe this service.'),\n featureColumn(\n 'b_s3',\n 'Service three',\n 'Describe this service.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'about-us',\n displayName: 'About Us',\n slug: 'about-us',\n seo: { title: 'About us' },\n nodes: buildNodes([\n // Was `maxWidth: 720` — the one unambiguous PROSE band in the set,\n // and the case the Prose Container preset exists for. MD (900px)\n // lands near the 65–75 characters a line that reads comfortably.\n section('a_wrapSection', 'md', 8, [\n {\n id: 'a_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('a_title', 'h3', 'About us'),\n text(\n 'a_body',\n 'body1',\n 'Share your story: how you started, what you believe, and ' +\n 'why customers trust you.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'contact-us',\n displayName: 'Contact Us',\n slug: 'contact-us',\n seo: { title: 'Contact' },\n nodes: buildNodes([\n // Was `maxWidth: 560`. Same reading as the landing page's contact\n // band: text plus a form column, so MD.\n section('c_wrapSection', 'md', 8, [\n {\n id: 'c_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('c_title', 'h3', 'Contact us'),\n text(\n 'c_body',\n 'body1',\n 'Questions or quotes — send a message and we reply within ' +\n 'one business day.',\n ),\n contactForm('c_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'portfolio',\n displayName: 'Portfolio',\n description: 'Personal portfolio: intro, work grid, and contact form.',\n category: 'Personal',\n screens: [\n {\n key: 'portfolio',\n displayName: 'Portfolio',\n slug: 'portfolio',\n seo: { title: 'Portfolio' },\n nodes: buildNodes([\n heroSection(\n 'p_',\n 'Hi, I make things',\n 'Designer / developer / photographer — introduce yourself here.',\n ),\n section('p_gridSection', 'xl', 6, [\n {\n id: 'p_grid',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n {\n id: 'p_img1',\n componentId: 'image',\n // `height` is an image's own intrinsic sizing prop, not a\n // band cap — out of scope for the container standard.\n props: { alt: 'Project one', height: '220px' },\n },\n {\n id: 'p_img2',\n componentId: 'image',\n props: { alt: 'Project two', height: '220px' },\n },\n {\n id: 'p_img3',\n componentId: 'image',\n props: { alt: 'Project three', height: '220px' },\n },\n ],\n },\n ]),\n // Was `maxWidth: 560`, the third of the three contact bands.\n section('p_contactSection', 'md', 6, [\n {\n id: 'p_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('p_contactTitle', 'h4', 'Work with me'),\n contactForm('p_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'physical-shop',\n displayName: 'Shop (physical products)',\n description:\n 'Storefront starter: home with featured products, filterable shop, ' +\n 'product page, cart, and customer accounts. After applying, set the ' +\n 'Product page as the product template in Store settings.',\n category: 'Commerce',\n screens: shopScreens('ps_', false),\n },\n {\n id: 'digital-shop',\n displayName: 'Shop (digital products)',\n description:\n 'Digital storefront starter: downloads-focused home, shop, product ' +\n 'page, cart, and accounts with a newsletter capture. Set the Product ' +\n 'page as the product template in Store settings after applying.',\n category: 'Commerce',\n screens: shopScreens('ds_', true),\n },\n]\n"],"names":["CANVAS_ROOT_ELEMENT_ID","normalizeScreenSlug","SCREEN_ROOT_PATH","starterTemplateDocId","starterId","screenKey","buildStarterTemplateDocs","starter","screens","map","screen","index","slug","id","key","data","kind","displayName","description","category","seo","nodes","source","type","starterName","starterDescription","starterOrder","buildAllStarterTemplateDocs","STARTER_TEMPLATES","flatMap","buildNodes","children","$id","componentId","child","walk","spec","parentId","pluginId","props","sx","text","variant","extra","section","maxWidth","verticalPadding","paddingTop","paddingBottom","heroSection","prefix","headline","tagline","spacing","alignItems","align","size","featureColumn","title","body","flex","paddingRight","paddingLeft","contactForm","formName","submitLabel","successMessage","fieldName","label","required","fieldType","commerceBlock","shopScreens","digital","sort","columns","maxItems","heading","showFilters","showCoupon","signedOutHeading","buildDefaultHomeScreen","siteName","name","trim","color","direction","alt","height"],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
* ONE JURISDICTION'S FIGURES, and the working papers behind them (AGL-2329).
|
|
19
|
+
*
|
|
20
|
+
* The operator's sales tax return sums its own invoices, and every plugin
|
|
21
|
+
* that sells through the platform answers for its own sales beside them
|
|
22
|
+
* (`plugin-manager/plugin-tax-return-sources.ts`). Each of those halves keys
|
|
23
|
+
* its money by the buyer's jurisdiction and states which rate produced it —
|
|
24
|
+
* and two implementations of "which rate produced this" is how two halves of
|
|
25
|
+
* one filing come to disagree. So the shape, the jurisdiction key and the
|
|
26
|
+
* fold live here once, and every half of the return uses them.
|
|
27
|
+
*
|
|
28
|
+
* Pure and total: no Firestore, no clock, no throw on a garbage row.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* One tax line, as the platform's invoice rows and a plugin's tax rows both
|
|
32
|
+
* write it. Three of these fields carry the comment "for the working papers"
|
|
33
|
+
* at the writer.
|
|
34
|
+
*/
|
|
35
|
+
export interface TaxLineInput {
|
|
36
|
+
amountCents?: unknown;
|
|
37
|
+
taxableAmountCents?: unknown;
|
|
38
|
+
taxabilityReason?: unknown;
|
|
39
|
+
taxRateId?: unknown;
|
|
40
|
+
/** The rate's percentage and its own jurisdiction, where the writer states them. */
|
|
41
|
+
percentage?: unknown;
|
|
42
|
+
rateState?: unknown;
|
|
43
|
+
jurisdiction?: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One rate that touched a jurisdiction.
|
|
47
|
+
*
|
|
48
|
+
* A return that states a jurisdiction's total and cannot state WHICH RATE
|
|
49
|
+
* produced it cannot be checked against a rate table, which is the first
|
|
50
|
+
* thing an examiner does.
|
|
51
|
+
*/
|
|
52
|
+
export interface TaxReturnRate {
|
|
53
|
+
/** Stripe's rate id, or `unknown` for a line that states none. */
|
|
54
|
+
taxRateId: string;
|
|
55
|
+
/** The rate as a percentage, when the line states one. */
|
|
56
|
+
percentage: number | null;
|
|
57
|
+
/** The rate's own state, which can differ from the customer's. */
|
|
58
|
+
rateState: string | null;
|
|
59
|
+
/** The rate's own jurisdiction label, as Stripe worded it. */
|
|
60
|
+
jurisdiction: string | null;
|
|
61
|
+
lines: number;
|
|
62
|
+
taxableAmountCents: number;
|
|
63
|
+
taxCollectedCents: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Why tax came out the way it did, for one jurisdiction.
|
|
67
|
+
*
|
|
68
|
+
* Keyed by Stripe's `taxability_reason` — `standard_rated`,
|
|
69
|
+
* `not_collecting`, `product_exempt`, `reverse_charge`, and the rest. $0 of
|
|
70
|
+
* tax reads identically whether the filer is unregistered there, the product
|
|
71
|
+
* is exempt, or the rate is genuinely zero, and those have three different
|
|
72
|
+
* answers — this is the detail a total can never carry.
|
|
73
|
+
*/
|
|
74
|
+
export interface TaxReturnTaxability {
|
|
75
|
+
lines: number;
|
|
76
|
+
taxableAmountCents: number;
|
|
77
|
+
taxCollectedCents: number;
|
|
78
|
+
}
|
|
79
|
+
export interface TaxReturnJurisdiction {
|
|
80
|
+
transactionCount: number;
|
|
81
|
+
/** Receipts excluding tax. */
|
|
82
|
+
totalSalesCents: number;
|
|
83
|
+
/** The base the rate was applied to. */
|
|
84
|
+
taxableSalesCents: number;
|
|
85
|
+
taxCollectedCents: number;
|
|
86
|
+
/** THE WORKING PAPERS — see {@link TaxReturnTaxability}. */
|
|
87
|
+
taxabilityReasons: Record<string, TaxReturnTaxability>;
|
|
88
|
+
/** Every rate that touched this jurisdiction, dearest first. */
|
|
89
|
+
rates: TaxReturnRate[];
|
|
90
|
+
}
|
|
91
|
+
/** An empty jurisdiction bucket, working papers included. */
|
|
92
|
+
export declare function emptyTaxJurisdiction(): TaxReturnJurisdiction;
|
|
93
|
+
/**
|
|
94
|
+
* The bucket key for a buyer's address: `COUNTRY-STATE`, the country alone
|
|
95
|
+
* when no subdivision is stated, and `unknown` when no country is.
|
|
96
|
+
*
|
|
97
|
+
* One derivation for every half of the return, so two halves can never key
|
|
98
|
+
* the same state differently. `unknown` is not a place: it is the part of a
|
|
99
|
+
* total that demonstrably cannot be placed, and every caller counts it.
|
|
100
|
+
*/
|
|
101
|
+
export declare function taxJurisdictionKey(address: {
|
|
102
|
+
country?: unknown;
|
|
103
|
+
state?: unknown;
|
|
104
|
+
} | null | undefined): string;
|
|
105
|
+
/** A Date from a JS Date or a Firestore-Timestamp-shaped `{ toDate() }`. */
|
|
106
|
+
export declare function asRowDate(value: unknown): Date | null;
|
|
107
|
+
/** `12345` → `"123.45"`. Dollars, because a return is filed in dollars. */
|
|
108
|
+
export declare function centsToDollars(cents: unknown): string;
|
|
109
|
+
/**
|
|
110
|
+
* Fold one row's tax lines into a jurisdiction's working papers.
|
|
111
|
+
*
|
|
112
|
+
* A line stating no reason is filed under `unstated` rather than dropped or
|
|
113
|
+
* folded into `standard_rated`. Dropping it would make the reasons fail to
|
|
114
|
+
* sum to the jurisdiction's tax — a working paper that does not reconcile is
|
|
115
|
+
* worse than none — and guessing `standard_rated` would assert a fact about
|
|
116
|
+
* a filing that nobody recorded.
|
|
117
|
+
*/
|
|
118
|
+
export declare function accumulateTaxWorkingPapers(bucket: TaxReturnJurisdiction, lines: readonly (TaxLineInput | undefined)[]): void;
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
* ONE JURISDICTION'S FIGURES, and the working papers behind them (AGL-2329).
|
|
18
|
+
*
|
|
19
|
+
* The operator's sales tax return sums its own invoices, and every plugin
|
|
20
|
+
* that sells through the platform answers for its own sales beside them
|
|
21
|
+
* (`plugin-manager/plugin-tax-return-sources.ts`). Each of those halves keys
|
|
22
|
+
* its money by the buyer's jurisdiction and states which rate produced it —
|
|
23
|
+
* and two implementations of "which rate produced this" is how two halves of
|
|
24
|
+
* one filing come to disagree. So the shape, the jurisdiction key and the
|
|
25
|
+
* fold live here once, and every half of the return uses them.
|
|
26
|
+
*
|
|
27
|
+
* Pure and total: no Firestore, no clock, no throw on a garbage row.
|
|
28
|
+
*/ /**
|
|
29
|
+
* One tax line, as the platform's invoice rows and a plugin's tax rows both
|
|
30
|
+
* write it. Three of these fields carry the comment "for the working papers"
|
|
31
|
+
* at the writer.
|
|
32
|
+
*/ /** An empty jurisdiction bucket, working papers included. */ export function emptyTaxJurisdiction() {
|
|
33
|
+
return {
|
|
34
|
+
transactionCount: 0,
|
|
35
|
+
totalSalesCents: 0,
|
|
36
|
+
taxableSalesCents: 0,
|
|
37
|
+
taxCollectedCents: 0,
|
|
38
|
+
taxabilityReasons: {},
|
|
39
|
+
rates: []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The bucket key for a buyer's address: `COUNTRY-STATE`, the country alone
|
|
44
|
+
* when no subdivision is stated, and `unknown` when no country is.
|
|
45
|
+
*
|
|
46
|
+
* One derivation for every half of the return, so two halves can never key
|
|
47
|
+
* the same state differently. `unknown` is not a place: it is the part of a
|
|
48
|
+
* total that demonstrably cannot be placed, and every caller counts it.
|
|
49
|
+
*/ export function taxJurisdictionKey(address) {
|
|
50
|
+
const country = address == null ? void 0 : address.country;
|
|
51
|
+
const state = address == null ? void 0 : address.state;
|
|
52
|
+
return typeof country === 'string' && country ? `${country}${typeof state === 'string' && state ? `-${state}` : ''}` : 'unknown';
|
|
53
|
+
}
|
|
54
|
+
/** A Date from a JS Date or a Firestore-Timestamp-shaped `{ toDate() }`. */ export function asRowDate(value) {
|
|
55
|
+
if (value instanceof Date) return value;
|
|
56
|
+
const toDate = value == null ? void 0 : value.toDate;
|
|
57
|
+
if (typeof toDate === 'function') {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = toDate.call(value);
|
|
60
|
+
return parsed instanceof Date ? parsed : null;
|
|
61
|
+
} catch (unused) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/** `12345` → `"123.45"`. Dollars, because a return is filed in dollars. */ export function centsToDollars(cents) {
|
|
68
|
+
const parsed = Number(cents != null ? cents : 0);
|
|
69
|
+
return ((Number.isFinite(parsed) ? parsed : 0) / 100).toFixed(2);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Fold one row's tax lines into a jurisdiction's working papers.
|
|
73
|
+
*
|
|
74
|
+
* A line stating no reason is filed under `unstated` rather than dropped or
|
|
75
|
+
* folded into `standard_rated`. Dropping it would make the reasons fail to
|
|
76
|
+
* sum to the jurisdiction's tax — a working paper that does not reconcile is
|
|
77
|
+
* worse than none — and guessing `standard_rated` would assert a fact about
|
|
78
|
+
* a filing that nobody recorded.
|
|
79
|
+
*/ export function accumulateTaxWorkingPapers(bucket, lines) {
|
|
80
|
+
for (const line of lines){
|
|
81
|
+
var _line_taxableAmountCents, _line_amountCents, _bucket_taxabilityReasons, _reason, _;
|
|
82
|
+
if (!line) continue;
|
|
83
|
+
const taxable = Math.round(Number((_line_taxableAmountCents = line.taxableAmountCents) != null ? _line_taxableAmountCents : 0)) || 0;
|
|
84
|
+
const collected = Math.round(Number((_line_amountCents = line.amountCents) != null ? _line_amountCents : 0)) || 0;
|
|
85
|
+
const reason = typeof line.taxabilityReason === 'string' && line.taxabilityReason ? line.taxabilityReason : 'unstated';
|
|
86
|
+
const entry = (_ = (_bucket_taxabilityReasons = bucket.taxabilityReasons)[_reason = reason]) != null ? _ : _bucket_taxabilityReasons[_reason] = {
|
|
87
|
+
lines: 0,
|
|
88
|
+
taxableAmountCents: 0,
|
|
89
|
+
taxCollectedCents: 0
|
|
90
|
+
};
|
|
91
|
+
entry.lines += 1;
|
|
92
|
+
entry.taxableAmountCents += taxable;
|
|
93
|
+
entry.taxCollectedCents += collected;
|
|
94
|
+
const taxRateId = typeof line.taxRateId === 'string' && line.taxRateId ? line.taxRateId : 'unknown';
|
|
95
|
+
const percentage = Number.isFinite(Number(line.percentage)) ? Number(line.percentage) : null;
|
|
96
|
+
const rateState = typeof line.rateState === 'string' && line.rateState ? line.rateState : null;
|
|
97
|
+
const jurisdictionLabel = typeof line.jurisdiction === 'string' && line.jurisdiction ? line.jurisdiction : null;
|
|
98
|
+
// Keyed by rate id AND percentage: a rate id whose percentage changed
|
|
99
|
+
// mid-period is two different rates on a return, and merging them would
|
|
100
|
+
// hide exactly the change an examiner is checking for.
|
|
101
|
+
let rate = bucket.rates.find((existing)=>existing.taxRateId === taxRateId && existing.percentage === percentage);
|
|
102
|
+
if (!rate) {
|
|
103
|
+
rate = {
|
|
104
|
+
taxRateId,
|
|
105
|
+
percentage,
|
|
106
|
+
rateState,
|
|
107
|
+
jurisdiction: jurisdictionLabel,
|
|
108
|
+
lines: 0,
|
|
109
|
+
taxableAmountCents: 0,
|
|
110
|
+
taxCollectedCents: 0
|
|
111
|
+
};
|
|
112
|
+
bucket.rates.push(rate);
|
|
113
|
+
}
|
|
114
|
+
rate.lines += 1;
|
|
115
|
+
rate.taxableAmountCents += taxable;
|
|
116
|
+
rate.taxCollectedCents += collected;
|
|
117
|
+
}
|
|
118
|
+
// Dearest first: the rate carrying the most money is the one a reviewer
|
|
119
|
+
// checks, and it belongs at the top rather than wherever it was first seen.
|
|
120
|
+
bucket.rates.sort((a, b)=>b.taxCollectedCents - a.taxCollectedCents || a.taxRateId.localeCompare(b.taxRateId));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=tax-jurisdiction-figures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/tax-jurisdiction-figures.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 * ONE JURISDICTION'S FIGURES, and the working papers behind them (AGL-2329).\n *\n * The operator's sales tax return sums its own invoices, and every plugin\n * that sells through the platform answers for its own sales beside them\n * (`plugin-manager/plugin-tax-return-sources.ts`). Each of those halves keys\n * its money by the buyer's jurisdiction and states which rate produced it —\n * and two implementations of \"which rate produced this\" is how two halves of\n * one filing come to disagree. So the shape, the jurisdiction key and the\n * fold live here once, and every half of the return uses them.\n *\n * Pure and total: no Firestore, no clock, no throw on a garbage row.\n */\n\n/**\n * One tax line, as the platform's invoice rows and a plugin's tax rows both\n * write it. Three of these fields carry the comment \"for the working papers\"\n * at the writer.\n */\nexport interface TaxLineInput {\n amountCents?: unknown\n taxableAmountCents?: unknown\n taxabilityReason?: unknown\n taxRateId?: unknown\n /** The rate's percentage and its own jurisdiction, where the writer states them. */\n percentage?: unknown\n rateState?: unknown\n jurisdiction?: unknown\n}\n\n/**\n * One rate that touched a jurisdiction.\n *\n * A return that states a jurisdiction's total and cannot state WHICH RATE\n * produced it cannot be checked against a rate table, which is the first\n * thing an examiner does.\n */\nexport interface TaxReturnRate {\n /** Stripe's rate id, or `unknown` for a line that states none. */\n taxRateId: string\n /** The rate as a percentage, when the line states one. */\n percentage: number | null\n /** The rate's own state, which can differ from the customer's. */\n rateState: string | null\n /** The rate's own jurisdiction label, as Stripe worded it. */\n jurisdiction: string | null\n lines: number\n taxableAmountCents: number\n taxCollectedCents: number\n}\n\n/**\n * Why tax came out the way it did, for one jurisdiction.\n *\n * Keyed by Stripe's `taxability_reason` — `standard_rated`,\n * `not_collecting`, `product_exempt`, `reverse_charge`, and the rest. $0 of\n * tax reads identically whether the filer is unregistered there, the product\n * is exempt, or the rate is genuinely zero, and those have three different\n * answers — this is the detail a total can never carry.\n */\nexport interface TaxReturnTaxability {\n lines: number\n taxableAmountCents: number\n taxCollectedCents: number\n}\n\nexport interface TaxReturnJurisdiction {\n transactionCount: number\n /** Receipts excluding tax. */\n totalSalesCents: number\n /** The base the rate was applied to. */\n taxableSalesCents: number\n taxCollectedCents: number\n /** THE WORKING PAPERS — see {@link TaxReturnTaxability}. */\n taxabilityReasons: Record<string, TaxReturnTaxability>\n /** Every rate that touched this jurisdiction, dearest first. */\n rates: TaxReturnRate[]\n}\n\n/** An empty jurisdiction bucket, working papers included. */\nexport function emptyTaxJurisdiction(): TaxReturnJurisdiction {\n return {\n transactionCount: 0,\n totalSalesCents: 0,\n taxableSalesCents: 0,\n taxCollectedCents: 0,\n taxabilityReasons: {},\n rates: [],\n }\n}\n\n/**\n * The bucket key for a buyer's address: `COUNTRY-STATE`, the country alone\n * when no subdivision is stated, and `unknown` when no country is.\n *\n * One derivation for every half of the return, so two halves can never key\n * the same state differently. `unknown` is not a place: it is the part of a\n * total that demonstrably cannot be placed, and every caller counts it.\n */\nexport function taxJurisdictionKey(\n address: { country?: unknown; state?: unknown } | null | undefined,\n): string {\n const country = address?.country\n const state = address?.state\n return typeof country === 'string' && country\n ? `${country}${typeof state === 'string' && state ? `-${state}` : ''}`\n : 'unknown'\n}\n\n/** A Date from a JS Date or a Firestore-Timestamp-shaped `{ toDate() }`. */\nexport function asRowDate(value: unknown): Date | null {\n if (value instanceof Date) return value\n const toDate = (value as { toDate?: () => Date } | null | undefined)?.toDate\n if (typeof toDate === 'function') {\n try {\n const parsed = toDate.call(value)\n return parsed instanceof Date ? parsed : null\n } catch {\n return null\n }\n }\n return null\n}\n\n/** `12345` → `\"123.45\"`. Dollars, because a return is filed in dollars. */\nexport function centsToDollars(cents: unknown): string {\n const parsed = Number(cents ?? 0)\n return ((Number.isFinite(parsed) ? parsed : 0) / 100).toFixed(2)\n}\n\n/**\n * Fold one row's tax lines into a jurisdiction's working papers.\n *\n * A line stating no reason is filed under `unstated` rather than dropped or\n * folded into `standard_rated`. Dropping it would make the reasons fail to\n * sum to the jurisdiction's tax — a working paper that does not reconcile is\n * worse than none — and guessing `standard_rated` would assert a fact about\n * a filing that nobody recorded.\n */\nexport function accumulateTaxWorkingPapers(\n bucket: TaxReturnJurisdiction,\n lines: readonly (TaxLineInput | undefined)[],\n): void {\n for (const line of lines) {\n if (!line) continue\n const taxable = Math.round(Number(line.taxableAmountCents ?? 0)) || 0\n const collected = Math.round(Number(line.amountCents ?? 0)) || 0\n\n const reason =\n typeof line.taxabilityReason === 'string' && line.taxabilityReason\n ? line.taxabilityReason\n : 'unstated'\n const entry = (bucket.taxabilityReasons[reason] ??= {\n lines: 0,\n taxableAmountCents: 0,\n taxCollectedCents: 0,\n })\n entry.lines += 1\n entry.taxableAmountCents += taxable\n entry.taxCollectedCents += collected\n\n const taxRateId =\n typeof line.taxRateId === 'string' && line.taxRateId\n ? line.taxRateId\n : 'unknown'\n const percentage = Number.isFinite(Number(line.percentage))\n ? Number(line.percentage)\n : null\n const rateState =\n typeof line.rateState === 'string' && line.rateState\n ? line.rateState\n : null\n const jurisdictionLabel =\n typeof line.jurisdiction === 'string' && line.jurisdiction\n ? line.jurisdiction\n : null\n // Keyed by rate id AND percentage: a rate id whose percentage changed\n // mid-period is two different rates on a return, and merging them would\n // hide exactly the change an examiner is checking for.\n let rate = bucket.rates.find(\n (existing) =>\n existing.taxRateId === taxRateId && existing.percentage === percentage,\n )\n if (!rate) {\n rate = {\n taxRateId,\n percentage,\n rateState,\n jurisdiction: jurisdictionLabel,\n lines: 0,\n taxableAmountCents: 0,\n taxCollectedCents: 0,\n }\n bucket.rates.push(rate)\n }\n rate.lines += 1\n rate.taxableAmountCents += taxable\n rate.taxCollectedCents += collected\n }\n // Dearest first: the rate carrying the most money is the one a reviewer\n // checks, and it belongs at the top rather than wherever it was first seen.\n bucket.rates.sort(\n (a, b) =>\n b.taxCollectedCents - a.taxCollectedCents ||\n a.taxRateId.localeCompare(b.taxRateId),\n )\n}\n"],"names":["emptyTaxJurisdiction","transactionCount","totalSalesCents","taxableSalesCents","taxCollectedCents","taxabilityReasons","rates","taxJurisdictionKey","address","country","state","asRowDate","value","Date","toDate","parsed","call","centsToDollars","cents","Number","isFinite","toFixed","accumulateTaxWorkingPapers","bucket","lines","line","reason","taxable","Math","round","taxableAmountCents","collected","amountCents","taxabilityReason","entry","taxRateId","percentage","rateState","jurisdictionLabel","jurisdiction","rate","find","existing","push","sort","a","b","localeCompare"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import type { HostTheme } from '@aglyn/shared-data-types';
|
|
18
|
-
import { type ThemeHostDocument } from './
|
|
18
|
+
import { type ThemeHostDocument } from './site-theme';
|
|
19
19
|
/**
|
|
20
20
|
* A site's theme library (AGL-3404): the themes a site can switch between,
|
|
21
21
|
* and the rule that switching, editing and saving never destroy one.
|
|
@@ -94,8 +94,15 @@ export declare const DEFAULT_THEME_ENTRY_ID = "default";
|
|
|
94
94
|
* a fixed id cannot collide with another.
|
|
95
95
|
*/
|
|
96
96
|
export declare const SITE_THEME_ENTRY_ID = "site-theme";
|
|
97
|
-
/**
|
|
98
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The default's name, wherever the library names it.
|
|
99
|
+
*
|
|
100
|
+
* Not "Material UI": the platform default keeps MUI's palette but draws it in
|
|
101
|
+
* the platform's own type and component defaults, and stock Material UI is a
|
|
102
|
+
* built-in theme of its own (AGL-3422). A console shows the operator's brand
|
|
103
|
+
* name in its place.
|
|
104
|
+
*/
|
|
105
|
+
export declare const DEFAULT_THEME_NAME = "Platform default";
|
|
99
106
|
/** A preset's entry id, which holds only its stash. */
|
|
100
107
|
export declare function presetEntryId(presetId: string): string;
|
|
101
108
|
/** A marketplace theme's entry id: one per listing, so reinstalling updates it. */
|