@aglyn/aglyn 1.0.0-beta.205 → 1.0.0-beta.207

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.
Files changed (44) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/console-routes.d.ts +14 -0
  3. package/src/lib/app-utils/console-routes.js +11 -0
  4. package/src/lib/app-utils/console-routes.js.map +1 -1
  5. package/src/lib/app-utils/content-authors.d.ts +12 -64
  6. package/src/lib/app-utils/content-authors.js +0 -133
  7. package/src/lib/app-utils/content-authors.js.map +1 -1
  8. package/src/lib/app-utils/docs-help.generated.d.ts +6 -0
  9. package/src/lib/app-utils/docs-help.generated.js +61 -0
  10. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  11. package/src/lib/app-utils/docs-index.generated.js +10 -3
  12. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  13. package/src/lib/app-utils/health-report.js +13 -0
  14. package/src/lib/app-utils/health-report.js.map +1 -1
  15. package/src/lib/app-utils/host-transfer.d.ts +55 -0
  16. package/src/lib/app-utils/host-transfer.js +23 -0
  17. package/src/lib/app-utils/host-transfer.js.map +1 -0
  18. package/src/lib/app-utils/local-business.d.ts +161 -0
  19. package/src/lib/app-utils/local-business.js +253 -0
  20. package/src/lib/app-utils/local-business.js.map +1 -0
  21. package/src/lib/app-utils/notifications.d.ts +1 -13
  22. package/src/lib/app-utils/notifications.js +1 -16
  23. package/src/lib/app-utils/notifications.js.map +1 -1
  24. package/src/lib/app-utils/operator-alerts.d.ts +132 -0
  25. package/src/lib/app-utils/operator-alerts.js +513 -0
  26. package/src/lib/app-utils/operator-alerts.js.map +1 -0
  27. package/src/lib/app-utils/server.d.ts +1 -0
  28. package/src/lib/app-utils/server.js +9 -0
  29. package/src/lib/app-utils/server.js.map +1 -1
  30. package/src/lib/app-utils/site-entity-json-ld.d.ts +96 -0
  31. package/src/lib/app-utils/site-entity-json-ld.js +220 -0
  32. package/src/lib/app-utils/site-entity-json-ld.js.map +1 -0
  33. package/src/lib/foundation/definitions/platform.types.d.ts +24 -0
  34. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  35. package/src/lib/plugin-manager/feature-plugins.d.ts +9 -0
  36. package/src/lib/plugin-manager/feature-plugins.js +9 -0
  37. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  38. package/src/lib/plugin-manager/first-party-plugins.generated.js +8 -1
  39. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  40. package/src/lib/plugin-manager/operator-alerts.d.ts +37 -0
  41. package/src/lib/plugin-manager/operator-alerts.js +80 -0
  42. package/src/lib/plugin-manager/operator-alerts.js.map +1 -0
  43. package/src/lib/plugin-manager/plugin-host-collections.d.ts +11 -0
  44. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2022 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 * Server-safe app-utils: every app-util EXCEPT the client-only React\n * contexts (see ./contexts). Nothing here calls `createContext` at module\n * scope, so this barrel — re-exported by `@aglyn/aglyn/server` — is safe to\n * import from tenant Server Components (AGL-405). The full `./index` barrel\n * re-adds the contexts for client consumers.\n */\nexport * from './binding-token-catalog'\nexport * from './binding-tokens'\nexport * from './breadcrumb-json-ld'\n// What a search listing holds and how long each field may be (AGL-2910):\n// the SEO editors and anything that proposes a value read one catalog.\nexport * from './seo-listing-fields'\n// `VideoObject` for the Video elements a page actually ships (AGL-2747).\n// Beside the other structured-data builders, and server-only like them: only\n// the route emits JSON-LD, and a crawler-facing block has no client caller.\nexport * from './video-object'\nexport * from './collection-delete'\nexport * from './collection-entries'\nexport * from './collection-kind'\nexport * from './collection-slug'\nexport * from './content-authors'\nexport * from './content-query-fields'\nexport * from './content-schema-type'\nexport * from './content-author-profile'\nexport * from './url-slug'\nexport * from './child-contract'\nexport * from './child-contract-compose'\n// The one reading of `restrictChildren`/`restrictParent`, shared by the\n// besigner's drop check and the AI plugin's node-tree validator (AGL-2905).\nexport * from './lineal-order'\n// What a node tree must satisfy before it lands on a canvas that did not\n// author it (AGL-2939): the marketplace's install sanitizer and the AI\n// plugin's node-tree validator both pass through it.\nexport * from './node-definition-sanitizer'\nexport * from './console-routes'\nexport * from './console-record-links'\n// What a stored screen-link value means, with no React attached — the\n// where-used scan reads these on the server (AGL-703).\nexport * from './screen-link-value'\n// Which entries a page's links name (AGL-3118). The tenant render reads\n// exactly those entry documents, so the walk runs on the server.\nexport * from './link-references'\nexport * from './analytics-path-key'\nexport * from './contacts'\n// A lead's or a contact's email state (AGL-3245): the last verdict on its\n// address, in words a person can act on, mirrored from the senders' lists.\nexport * from './email-state'\n// The records that sit beside a contact — companies, pipelines, deals, tasks,\n// activities, field definitions (AGL-2595). Pure like `contacts`, and beside\n// it because `ContactFacet` names its lifecycle stage and custom-value types;\n// the scope stamp every CRM creator writes lives here too.\nexport * from './crm'\n// Email templates, snippets and the merge-field resolver (AGL-2658): the\n// letter a rep keeps under a name, and the one grammar the send route and\n// the dialog fill it in by. Pure like `crm`, and beside it because a\n// template is scoped the way a task is.\nexport * from './crm-email-templates'\n// The booking door's shared vocabulary (AGL-2660): the query keys a booking\n// link carries, the record reference inside it, and the wording and due\n// date of what a booking files back on the record.\nexport * from './crm-booking'\n// Two contact documents folded into one (AGL-2625): the plan the console\n// previews and the server writes, reading the facet shape from `contacts`\n// and the company mirror from `crm`.\nexport * from './contact-merge'\n// A person's privacy erasure from one workspace (AGL-2623): the request's\n// shape and id, the marker a record carries while it waits, and the lists\n// the dialog and the docs share. Pure like `crm`; the sweep is server-side.\nexport * from './person-erasure'\n// What the CRM's reports compute from what they read — periods, weekly\n// buckets, the lifecycle funnel, pipeline totals (AGL-2604). Pure like `crm`,\n// and beside it because it reads the stage list and the deal weighting from\n// there rather than restating either.\nexport * from './crm-reports'\n// Every CRM CSV, written once (AGL-2662): the column lists and the cells\n// each section's Export button writes, and the server's whole-collection\n// export streams — one implementation, because the console app may not\n// import the plugin whose sections used to hold it.\nexport * from './crm-csv'\n// Which holder a cross-site reader sees one contact through (AGL-2630),\n// beside the files that write it: the org-level list and the server's\n// export both flatten a contact this way.\nexport * from './contact-holder'\n// What one member is owed this morning — the day's window in a named zone,\n// the task and lead bucketing, and the words the notification and the mail\n// say (AGL-2619). Pure like `crm-reports`, read by the scheduled route.\nexport * from './crm-digest'\n// A task's reminder at its own time (AGL-2659): which reminder a save\n// leaves, whether one is owed, and the words the notification and the mail\n// say. Pure like `crm-digest`, read by the hourly route and by every\n// writer of the field.\nexport * from './crm-task-reminders'\n// Email capture (AGL-2657): the address's shape and token, which of a\n// message's addresses is the correspondent, the excerpt, and the row a\n// captured message becomes. Pure like `crm-digest`; the webhook route and\n// the address route are the readers with Firestore.\nexport * from './crm-inbound'\n// A member's own addresses in a workspace (AGL-2975): the ones they have\n// confirmed count as theirs wherever the roster is asked who wrote a\n// message. Pure; the store and the confirmation link are server-side.\nexport * from './member-email-aliases'\n// The next activity a record carries (AGL-2661): what `nextTaskAtMs` IS given\n// a record's open tasks, the view clause a list filters on, the report figure.\n// Pure like `crm`; the admin library's `recomputeCrmNextTaskAt` is the writer.\nexport * from './crm-next-activity'\n// The CSV cell grammar the exports write with (AGL-2621), and the target a\n// mapped column names for a custom field — named rather than starred, because\n// an importer built on this file exports the shared ceilings under its own\n// names.\nexport {\n csvCell,\n csvDocument,\n customImportTarget,\n customImportTargetKey,\n importNoMailServerSentence,\n} from './csv-import'\nexport type {\n ImportChunkResult,\n ImportDroppedValue,\n ImportSkippedRow,\n} from './csv-import'\n// What a VALUE under a custom contact field may be, and the coercion every\n// door applies to one (AGL-2601): a mapped form field, an API `custom` body,\n// and the carry-over that keeps a mapping across a publish. Pure like `crm`.\nexport * from './contact-custom-fields'\n// The consent JOIN (`docs/specs/email-overhaul.md` §3f). Pure, and it composes\n// `contacts` for the normalizer, so it sits directly beside it. No Node\n// builtin, which is what keeps it out of the `/server`-only group `person-key`\n// belongs to.\nexport * from './consent-groups'\nexport * from './marketing-consent'\n// The ENROLLMENT-time half of the same question, beside its reader for the\n// same reason: `marketing-consent` decides whether a recorded basis lets us\n// mail somebody, and this decides what basis putting them on a list may\n// record. Two enrollment surfaces import it — the Inbox assignment route and\n// the Emails console's audience card — and neither could import the other.\nexport * from './list-assignment-policy'\n// Reading the FILE a merchant arrives with (`docs/specs/email-competitive-gaps.md`\n// G5/P4), directly after the policy it hands its addresses to: parsing and\n// mechanical screening only, so that an import asks the enrollment question\n// through the same module the one-address add path asks it through rather\n// than answering it a second way.\nexport * from './list-import'\n// The dynamic-list rule (§3b/§3c), beside it for the same reasons: pure, and\n// it composes `contacts` for the segment vocabulary rather than restating it.\nexport * from './dynamic-list-rule'\n// The subscribable streams a recipient can leave one at a time\n// (`docs/specs/email-competitive-gaps.md` §1f). Pure, and read from all three\n// sides of the feature: the composer picks one, the send path signs it into\n// the unsubscribe link, and the unauthenticated preference page renders the\n// catalog. No Node builtin, so it stays out of the `/server`-only group.\nexport * from './email-topics'\nexport * from './compose-layout-nodes'\n// Layout properties applied through the chain (AGL-2893).\nexport * from './compose-layout-props'\n// A screen's per-page restyling of its layout's elements (AGL-3286).\nexport * from './layout-style-overrides'\nexport * from './document-landmark'\nexport * from './functions'\nexport * from './compose-reusable-components'\n// Where a reusable component is placed — pages or emails (AGL-3287).\nexport * from './reusable-component-kind'\nexport * from './compress'\nexport * from './merge-node-sx'\nexport * from './palette-sx'\nexport * from './scheme-sx'\n// An element pinned to one color scheme (AGL-3284).\nexport * from './element-color-scheme'\nexport * from './create-resource-uid'\nexport * from './decompress'\nexport * from './stored-nodes'\nexport * from './strip-undefined'\nexport * from './organizations'\nexport * from './org-permissions'\n// The AI verdict on either membership axis (AGL-2927). After both modules it\n// composes: the org catalog for org-wide members, the host role for site\n// collaborators.\nexport * from './host-permissions'\nexport * from './password-policy'\nexport * from './idp-profile'\nexport * from './onboarding-deep-link'\n// Where an account came from (AGL-1731). Beside the plan intent because\n// they are the same hop — the marketing CTA's query string — and both are\n// remembered on `users/{uid}` across the verification wall.\nexport * from './campaign-attribution'\n// The edge between a campaign and the forms, screens and contacts a push is\n// coordinated across. Beside the attribution above because the two answer\n// opposite questions about the same word: attribution records where somebody\n// CAME from, this records what a merchant DECLARED belongs together.\nexport * from './campaign-membership'\nexport * from './deployment-shape'\n// Which browser origins may complete a signed direct-to-GCS upload (AGL-1452).\n// GCS matches the origin list EXACTLY, so every serving console name needs its\n// own entry — the rule nobody could be expected to remember at attach time.\nexport * from './upload-cors'\n// The docs-help subset the first-party plugin consoles read (AGL-2213) — the\n// console's own registry lives in apps/console/constants and a lib cannot\n// import an app.\nexport * from './docs-help'\nexport * from './platform-brand'\nexport * from './plan-entitlements'\n// The free plan's bandwidth hard cap (AGL-1967/2070/2155). After\n// `plan-entitlements`, which owns the predicate it keys off.\nexport * from './bandwidth-cap'\nexport * from './form-abuse-ceiling'\nexport * from './forms'\n// What a form's DESIGN must still satisfy for its submissions to arrive.\n// After `forms`, whose field walk it reads the drawn fields with.\nexport * from './form-contract'\nexport * from './visitor-record-ceiling'\nexport * from './health-report'\n// Did a webhook delivery actually DO anything (AGL-1954)? After\n// `health-report`, whose billing verdict consumes the counts this produces.\nexport * from './webhook-delivery'\nexport * from './support-tiers'\nexport * from './release-flags'\n// Who runs THIS deployment, and where its notices go (AGL-2016). Ordered\n// before the four surfaces that print it, all of which used to declare their\n// own `support@aglyn.com` literal.\nexport * from './operator-identity'\nexport * from './lockdown'\nexport * from './media-quarantine'\nexport * from './media-takedown-reach'\nexport * from './upload-inspection'\nexport * from './abuse-report'\n// The other three quarters of §512 (AGL-1983): the put-back procedure that\n// answers a notice, and the strike ledger that conditions the safe harbour.\nexport * from './dmca-counter-notice'\nexport * from './repeat-infringer'\n// `addBusinessDays` briefly existed TWICE — once here in './support-tiers'\n// and once in './dmca-counter-notice' — and two `export *` made the name\n// ambiguous, TS2308'ing this barrel and every tsconfig that reads it. The\n// interleave was fixed twice in parallel: once by naming a winner here, and\n// once by deleting the second definition so './dmca-counter-notice' imports\n// this one. Only the deletion survives, so the disambiguating re-export that\n// stood on this line is gone with it — `export *` is unambiguous again.\n//\n// The concern that argued for keeping two copies is worth answering rather\n// than dropping: a later tweak to the SUPPORT SLA window must never silently\n// move the §512(g)(2)(C) put-back deadline. It cannot. This function embeds\n// no window — it is \"add N business days\", and N is the argument. The two\n// windows live in their own constants (`SUPPORT_*` there,\n// `COUNTER_NOTICE_*_BUSINESS_DAYS` in the DMCA module) and never meet. What\n// SHOULD stay shared is the day arithmetic itself: two copies of a weekend\n// rule are two chances to disagree about which day a deadline falls on.\nexport * from './org-override-reason'\nexport * from './host-tokens'\nexport * from './variables'\nexport * from './workflows'\nexport * from './datasets'\nexport * from './expand-repeatables'\nexport * from './org-roles'\nexport * from './markdown-lite'\nexport * from './notifications'\nexport * from './definition-canvas-tree'\nexport * from './ensure-canvas-root'\nexport * from './repair-canvas-nodes'\nexport * from './detect-template-placeholders'\nexport * from './guarded-version-save'\nexport * from './local-storage-budget'\nexport * from './measure-node-map'\nexport * from './resolve-named-tokens'\nexport * from './screen-route'\nexport * from './host-naming'\nexport * from './name-search'\nexport * from './artifact-list-keys'\nexport * from './name-match'\nexport * from './api-plugins'\nexport * from './plugin-api-rate-limit'\n// The same-origin gate on visitor-facing plugin writes (AGL-1880). After\n// `plugin-api-rate-limit`, whose `isMachinePluginApiPath` it reuses so the\n// two gates exempt exactly the same machine surfaces.\nexport * from './plugin-api-cross-origin'\nexport * from './actions'\nexport * from './element-animation'\nexport * from './attribution-guard'\nexport * from './node-interactions'\nexport * from './element-ui'\nexport * from './image-upload-types'\nexport * from './sanitize-svg'\nexport * from './media-folders'\nexport * from './media-metadata'\nexport * from './media-ref'\n// After `media-ref`, whose reference grammar it reads stored URLs with.\nexport * from './paid-media-source'\nexport * from './author-css'\n// The isomorphic HTML rule (AGL-1901), after the CSS one it depends on.\nexport * from './author-html'\nexport * from './dataset-models'\nexport * from './dataset-csv'\nexport * from './marketplace-provenance'\nexport * from './marketplace-update-state'\nexport * from './dataset-query'\nexport * from './plugin-manifest'\n// After `plugin-manifest`, whose revocation predicates it asks the kill\n// question with, and after `media-ref`, whose grammar decides what counts as a\n// first-party image.\nexport * from './email-starter-policy'\nexport * from './safe-json-ld'\n// Agent readiness (AGL-2716): content negotiation, the Markdown rendering of a\n// page, `/llms.txt` and the per-site OpenAPI document. All pure, all server-safe.\nexport * from './accept-negotiation'\nexport * from './agent-api-catalog'\nexport * from './agent-openapi'\nexport * from './author-html-markdown'\nexport * from './llms-txt'\nexport * from './page-markdown'\nexport * from './request-geo'\nexport * from './request-ip'\nexport * from './search-indexing'\nexport * from './seo-title'\nexport * from './sitemap'\nexport * from './visitor-consent'\nexport * from './social-image'\nexport * from './scope-tokens'\nexport * from './org-billing-doc'\nexport * from './stripe-deployment-mode'\nexport * from './deferred-image'\n"],"names":["csvCell","csvDocument","customImportTarget","customImportTargetKey","importNoMailServerSentence"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2022 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 * Server-safe app-utils: every app-util EXCEPT the client-only React\n * contexts (see ./contexts). Nothing here calls `createContext` at module\n * scope, so this barrel — re-exported by `@aglyn/aglyn/server` — is safe to\n * import from tenant Server Components (AGL-405). The full `./index` barrel\n * re-adds the contexts for client consumers.\n */\nexport * from './binding-token-catalog'\nexport * from './binding-tokens'\nexport * from './breadcrumb-json-ld'\n// What a search listing holds and how long each field may be (AGL-2910):\n// the SEO editors and anything that proposes a value read one catalog.\nexport * from './seo-listing-fields'\n// `VideoObject` for the Video elements a page actually ships (AGL-2747).\n// Beside the other structured-data builders, and server-only like them: only\n// the route emits JSON-LD, and a crawler-facing block has no client caller.\nexport * from './video-object'\nexport * from './collection-delete'\nexport * from './collection-entries'\nexport * from './collection-kind'\nexport * from './collection-slug'\nexport * from './content-authors'\n// NOT re-exported: `./local-business` and `./site-entity-json-ld` (AGL-3383).\n// This barrel is the whole namespace a realm bundle is handed and part of the\n// runtime a published page loads (`check:tenant-wire-weight`); the site\n// entity's JSON-LD and the LocalBusiness allow-list are read only by the\n// tenant page's server render and the console's Setup → SEO card, which import\n// them by path (`@aglyn/aglyn/app-utils/...`).\nexport * from './content-query-fields'\nexport * from './content-schema-type'\nexport * from './content-author-profile'\nexport * from './url-slug'\nexport * from './child-contract'\nexport * from './child-contract-compose'\n// The one reading of `restrictChildren`/`restrictParent`, shared by the\n// besigner's drop check and the AI plugin's node-tree validator (AGL-2905).\nexport * from './lineal-order'\n// What a node tree must satisfy before it lands on a canvas that did not\n// author it (AGL-2939): the marketplace's install sanitizer and the AI\n// plugin's node-tree validator both pass through it.\nexport * from './node-definition-sanitizer'\nexport * from './console-routes'\nexport * from './console-record-links'\n// What a stored screen-link value means, with no React attached — the\n// where-used scan reads these on the server (AGL-703).\nexport * from './screen-link-value'\n// Which entries a page's links name (AGL-3118). The tenant render reads\n// exactly those entry documents, so the walk runs on the server.\nexport * from './link-references'\nexport * from './analytics-path-key'\nexport * from './contacts'\n// A lead's or a contact's email state (AGL-3245): the last verdict on its\n// address, in words a person can act on, mirrored from the senders' lists.\nexport * from './email-state'\n// The records that sit beside a contact — companies, pipelines, deals, tasks,\n// activities, field definitions (AGL-2595). Pure like `contacts`, and beside\n// it because `ContactFacet` names its lifecycle stage and custom-value types;\n// the scope stamp every CRM creator writes lives here too.\nexport * from './crm'\n// Email templates, snippets and the merge-field resolver (AGL-2658): the\n// letter a rep keeps under a name, and the one grammar the send route and\n// the dialog fill it in by. Pure like `crm`, and beside it because a\n// template is scoped the way a task is.\nexport * from './crm-email-templates'\n// The booking door's shared vocabulary (AGL-2660): the query keys a booking\n// link carries, the record reference inside it, and the wording and due\n// date of what a booking files back on the record.\nexport * from './crm-booking'\n// Two contact documents folded into one (AGL-2625): the plan the console\n// previews and the server writes, reading the facet shape from `contacts`\n// and the company mirror from `crm`.\nexport * from './contact-merge'\n// A person's privacy erasure from one workspace (AGL-2623): the request's\n// shape and id, the marker a record carries while it waits, and the lists\n// the dialog and the docs share. Pure like `crm`; the sweep is server-side.\nexport * from './person-erasure'\n// What the CRM's reports compute from what they read — periods, weekly\n// buckets, the lifecycle funnel, pipeline totals (AGL-2604). Pure like `crm`,\n// and beside it because it reads the stage list and the deal weighting from\n// there rather than restating either.\nexport * from './crm-reports'\n// Every CRM CSV, written once (AGL-2662): the column lists and the cells\n// each section's Export button writes, and the server's whole-collection\n// export streams — one implementation, because the console app may not\n// import the plugin whose sections used to hold it.\nexport * from './crm-csv'\n// Which holder a cross-site reader sees one contact through (AGL-2630),\n// beside the files that write it: the org-level list and the server's\n// export both flatten a contact this way.\nexport * from './contact-holder'\n// What one member is owed this morning — the day's window in a named zone,\n// the task and lead bucketing, and the words the notification and the mail\n// say (AGL-2619). Pure like `crm-reports`, read by the scheduled route.\nexport * from './crm-digest'\n// A task's reminder at its own time (AGL-2659): which reminder a save\n// leaves, whether one is owed, and the words the notification and the mail\n// say. Pure like `crm-digest`, read by the hourly route and by every\n// writer of the field.\nexport * from './crm-task-reminders'\n// Email capture (AGL-2657): the address's shape and token, which of a\n// message's addresses is the correspondent, the excerpt, and the row a\n// captured message becomes. Pure like `crm-digest`; the webhook route and\n// the address route are the readers with Firestore.\nexport * from './crm-inbound'\n// A member's own addresses in a workspace (AGL-2975): the ones they have\n// confirmed count as theirs wherever the roster is asked who wrote a\n// message. Pure; the store and the confirmation link are server-side.\nexport * from './member-email-aliases'\n// The next activity a record carries (AGL-2661): what `nextTaskAtMs` IS given\n// a record's open tasks, the view clause a list filters on, the report figure.\n// Pure like `crm`; the admin library's `recomputeCrmNextTaskAt` is the writer.\nexport * from './crm-next-activity'\n// The CSV cell grammar the exports write with (AGL-2621), and the target a\n// mapped column names for a custom field — named rather than starred, because\n// an importer built on this file exports the shared ceilings under its own\n// names.\nexport {\n csvCell,\n csvDocument,\n customImportTarget,\n customImportTargetKey,\n importNoMailServerSentence,\n} from './csv-import'\nexport type {\n ImportChunkResult,\n ImportDroppedValue,\n ImportSkippedRow,\n} from './csv-import'\n// What a VALUE under a custom contact field may be, and the coercion every\n// door applies to one (AGL-2601): a mapped form field, an API `custom` body,\n// and the carry-over that keeps a mapping across a publish. Pure like `crm`.\nexport * from './contact-custom-fields'\n// The consent JOIN (`docs/specs/email-overhaul.md` §3f). Pure, and it composes\n// `contacts` for the normalizer, so it sits directly beside it. No Node\n// builtin, which is what keeps it out of the `/server`-only group `person-key`\n// belongs to.\nexport * from './consent-groups'\nexport * from './marketing-consent'\n// The ENROLLMENT-time half of the same question, beside its reader for the\n// same reason: `marketing-consent` decides whether a recorded basis lets us\n// mail somebody, and this decides what basis putting them on a list may\n// record. Two enrollment surfaces import it — the Inbox assignment route and\n// the Emails console's audience card — and neither could import the other.\nexport * from './list-assignment-policy'\n// Reading the FILE a merchant arrives with (`docs/specs/email-competitive-gaps.md`\n// G5/P4), directly after the policy it hands its addresses to: parsing and\n// mechanical screening only, so that an import asks the enrollment question\n// through the same module the one-address add path asks it through rather\n// than answering it a second way.\nexport * from './list-import'\n// The dynamic-list rule (§3b/§3c), beside it for the same reasons: pure, and\n// it composes `contacts` for the segment vocabulary rather than restating it.\nexport * from './dynamic-list-rule'\n// The subscribable streams a recipient can leave one at a time\n// (`docs/specs/email-competitive-gaps.md` §1f). Pure, and read from all three\n// sides of the feature: the composer picks one, the send path signs it into\n// the unsubscribe link, and the unauthenticated preference page renders the\n// catalog. No Node builtin, so it stays out of the `/server`-only group.\nexport * from './email-topics'\nexport * from './compose-layout-nodes'\n// Layout properties applied through the chain (AGL-2893).\nexport * from './compose-layout-props'\n// A screen's per-page restyling of its layout's elements (AGL-3286).\nexport * from './layout-style-overrides'\nexport * from './document-landmark'\nexport * from './functions'\nexport * from './compose-reusable-components'\n// Where a reusable component is placed — pages or emails (AGL-3287).\nexport * from './reusable-component-kind'\nexport * from './compress'\nexport * from './merge-node-sx'\nexport * from './palette-sx'\nexport * from './scheme-sx'\n// An element pinned to one color scheme (AGL-3284).\nexport * from './element-color-scheme'\nexport * from './create-resource-uid'\nexport * from './decompress'\nexport * from './stored-nodes'\nexport * from './strip-undefined'\nexport * from './organizations'\nexport * from './org-permissions'\n// The AI verdict on either membership axis (AGL-2927). After both modules it\n// composes: the org catalog for org-wide members, the host role for site\n// collaborators.\nexport * from './host-permissions'\nexport * from './password-policy'\nexport * from './idp-profile'\nexport * from './onboarding-deep-link'\n// Where an account came from (AGL-1731). Beside the plan intent because\n// they are the same hop — the marketing CTA's query string — and both are\n// remembered on `users/{uid}` across the verification wall.\nexport * from './campaign-attribution'\n// The edge between a campaign and the forms, screens and contacts a push is\n// coordinated across. Beside the attribution above because the two answer\n// opposite questions about the same word: attribution records where somebody\n// CAME from, this records what a merchant DECLARED belongs together.\nexport * from './campaign-membership'\nexport * from './deployment-shape'\n// Which browser origins may complete a signed direct-to-GCS upload (AGL-1452).\n// GCS matches the origin list EXACTLY, so every serving console name needs its\n// own entry — the rule nobody could be expected to remember at attach time.\nexport * from './upload-cors'\n// The docs-help subset the first-party plugin consoles read (AGL-2213) — the\n// console's own registry lives in apps/console/constants and a lib cannot\n// import an app.\nexport * from './docs-help'\nexport * from './platform-brand'\nexport * from './plan-entitlements'\n// The free plan's bandwidth hard cap (AGL-1967/2070/2155). After\n// `plan-entitlements`, which owns the predicate it keys off.\nexport * from './bandwidth-cap'\nexport * from './form-abuse-ceiling'\nexport * from './forms'\n// What a form's DESIGN must still satisfy for its submissions to arrive.\n// After `forms`, whose field walk it reads the drawn fields with.\nexport * from './form-contract'\nexport * from './visitor-record-ceiling'\nexport * from './health-report'\n// Did a webhook delivery actually DO anything (AGL-1954)? After\n// `health-report`, whose billing verdict consumes the counts this produces.\nexport * from './webhook-delivery'\nexport * from './support-tiers'\nexport * from './release-flags'\n// Who runs THIS deployment, and where its notices go (AGL-2016). Ordered\n// before the four surfaces that print it, all of which used to declare their\n// own `support@aglyn.com` literal.\nexport * from './operator-identity'\nexport * from './lockdown'\nexport * from './media-quarantine'\nexport * from './media-takedown-reach'\nexport * from './upload-inspection'\nexport * from './abuse-report'\n// The other three quarters of §512 (AGL-1983): the put-back procedure that\n// answers a notice, and the strike ledger that conditions the safe harbour.\nexport * from './dmca-counter-notice'\nexport * from './repeat-infringer'\n// `addBusinessDays` briefly existed TWICE — once here in './support-tiers'\n// and once in './dmca-counter-notice' — and two `export *` made the name\n// ambiguous, TS2308'ing this barrel and every tsconfig that reads it. The\n// interleave was fixed twice in parallel: once by naming a winner here, and\n// once by deleting the second definition so './dmca-counter-notice' imports\n// this one. Only the deletion survives, so the disambiguating re-export that\n// stood on this line is gone with it — `export *` is unambiguous again.\n//\n// The concern that argued for keeping two copies is worth answering rather\n// than dropping: a later tweak to the SUPPORT SLA window must never silently\n// move the §512(g)(2)(C) put-back deadline. It cannot. This function embeds\n// no window — it is \"add N business days\", and N is the argument. The two\n// windows live in their own constants (`SUPPORT_*` there,\n// `COUNTER_NOTICE_*_BUSINESS_DAYS` in the DMCA module) and never meet. What\n// SHOULD stay shared is the day arithmetic itself: two copies of a weekend\n// rule are two chances to disagree about which day a deadline falls on.\nexport * from './org-override-reason'\nexport * from './host-tokens'\nexport * from './variables'\nexport * from './workflows'\nexport * from './datasets'\nexport * from './expand-repeatables'\nexport * from './org-roles'\nexport * from './markdown-lite'\nexport * from './notifications'\n// The operator alert registry (AGL-3377); OPERATOR_ALERT_NOTIFICATION_TYPES\n// lives here now, derived from it.\nexport * from './operator-alerts'\nexport * from './definition-canvas-tree'\nexport * from './ensure-canvas-root'\nexport * from './repair-canvas-nodes'\nexport * from './detect-template-placeholders'\nexport * from './guarded-version-save'\nexport * from './local-storage-budget'\nexport * from './measure-node-map'\nexport * from './resolve-named-tokens'\nexport * from './screen-route'\nexport * from './host-naming'\nexport * from './name-search'\nexport * from './artifact-list-keys'\nexport * from './name-match'\nexport * from './api-plugins'\nexport * from './plugin-api-rate-limit'\n// The same-origin gate on visitor-facing plugin writes (AGL-1880). After\n// `plugin-api-rate-limit`, whose `isMachinePluginApiPath` it reuses so the\n// two gates exempt exactly the same machine surfaces.\nexport * from './plugin-api-cross-origin'\nexport * from './actions'\nexport * from './element-animation'\nexport * from './attribution-guard'\nexport * from './node-interactions'\nexport * from './element-ui'\nexport * from './image-upload-types'\nexport * from './sanitize-svg'\nexport * from './media-folders'\nexport * from './media-metadata'\nexport * from './media-ref'\n// After `media-ref`, whose reference grammar it reads stored URLs with.\nexport * from './paid-media-source'\nexport * from './author-css'\n// The isomorphic HTML rule (AGL-1901), after the CSS one it depends on.\nexport * from './author-html'\nexport * from './dataset-models'\nexport * from './dataset-csv'\nexport * from './marketplace-provenance'\nexport * from './marketplace-update-state'\nexport * from './dataset-query'\nexport * from './plugin-manifest'\n// After `plugin-manifest`, whose revocation predicates it asks the kill\n// question with, and after `media-ref`, whose grammar decides what counts as a\n// first-party image.\nexport * from './email-starter-policy'\nexport * from './safe-json-ld'\n// Agent readiness (AGL-2716): content negotiation, the Markdown rendering of a\n// page, `/llms.txt` and the per-site OpenAPI document. All pure, all server-safe.\nexport * from './accept-negotiation'\nexport * from './agent-api-catalog'\nexport * from './agent-openapi'\nexport * from './author-html-markdown'\nexport * from './llms-txt'\nexport * from './page-markdown'\nexport * from './request-geo'\nexport * from './request-ip'\nexport * from './search-indexing'\nexport * from './seo-title'\nexport * from './sitemap'\nexport * from './visitor-consent'\nexport * from './social-image'\nexport * from './scope-tokens'\nexport * from './org-billing-doc'\nexport * from './stripe-deployment-mode'\nexport * from './deferred-image'\n"],"names":["csvCell","csvDocument","customImportTarget","customImportTargetKey","importNoMailServerSentence"],"mappings":"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"}
@@ -0,0 +1,96 @@
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
+ import { type ContentAuthorImageContext, type HostSeoEntity, type SiteEntityHost } from './content-authors';
18
+ import { type LocalBusinessType } from './local-business';
19
+ /**
20
+ * The `@type` the SITE entity publishes under — {@link contentAuthorSchemaType}
21
+ * with one refinement (AGL-3383): an entity that is not a Person and names a
22
+ * business type on the allow-list publishes as that type.
23
+ *
24
+ * Person wins over a business type, because `schema.org` gives a Person none
25
+ * of the properties a business type exists to carry. The stored `type` stays
26
+ * Organization or Person either way; every LocalBusiness subtype IS an
27
+ * Organization, so a consumer reading this as one is still right.
28
+ *
29
+ * Shared by the standalone node and the nested `publisher`, which carry the
30
+ * same `@id` and must therefore not disagree about what they are.
31
+ */
32
+ export declare function siteEntitySchemaType(entity: HostSeoEntity | null | undefined): 'Person' | 'Organization' | LocalBusinessType;
33
+ /**
34
+ * `schema.org` JSON-LD for the SITE entity (`host.seo.entity`) — the value
35
+ * `Article.publisher` and `WebSite.publisher` are built from.
36
+ *
37
+ * Lives beside {@link contentAuthorJsonLd} on purpose (AGL-2486): a site
38
+ * entity and a post author answer the same question about two different
39
+ * subjects, and the reason the Person branch was broken for two years is that
40
+ * the answer was written inline at its one call site where nothing could
41
+ * test it. One module, two entry points, one `Person`/`Organization`
42
+ * decision — {@link contentAuthorSchemaType}.
43
+ *
44
+ * `logo` is emitted verbatim on BOTH branches, unchanged from what the tenant
45
+ * has always published. It is `host.seo.entity.logo`, a plain URL field on
46
+ * the Setup form rather than a media-picker target, and re-keying it to
47
+ * `image` for a Person would change the publisher block on every page of
48
+ * every site that set one — a separate decision from this one.
49
+ */
50
+ export declare function hostSeoEntityJsonLd(entity: HostSeoEntity | null | undefined): Record<string, unknown> | undefined;
51
+ /**
52
+ * The site's publisher as a STANDALONE, fully populated `schema.org` node
53
+ * (AGL-2716).
54
+ *
55
+ * ## Why this exists next to {@link hostSeoEntityJsonLd}
56
+ *
57
+ * That function builds the value `publisher` takes: a `@type` and a `name`,
58
+ * nested inside a `WebSite` or an `Article`, gated on the author having filled
59
+ * in the Entity form. It is deliberately narrow, and every call site nests it.
60
+ *
61
+ * A consumer asking "who runs this site, and how do I reach them" — an AI
62
+ * assistant answering a contact question, a readiness audit checking whether a
63
+ * business is identifiable — looks for a TOP-LEVEL node of type `Organization`
64
+ * carrying `name`, `description`, `contactPoint` and `address`. A publisher
65
+ * nested two levels inside a `WebSite` answers a different question, and a
66
+ * publisher that is absent because nobody filled in a form answers none.
67
+ *
68
+ * So this one:
69
+ *
70
+ * - is emitted as its own `<script type="application/ld+json">`, at the top
71
+ * level, where it can be found without walking into another node;
72
+ * - FALLS BACK to the site's own identity when the Entity form is empty, so
73
+ * every site on the platform publishes a complete entity rather than only
74
+ * the sites whose author found the form;
75
+ * - carries the contact and address fields, which are what turn "a name" into
76
+ * "a business a consumer can verify".
77
+ *
78
+ * ## Why the fallback is safe
79
+ *
80
+ * It changes nothing that already exists: `hostSeoEntityJsonLd` still gates on
81
+ * `entity.name`, so `publisher` appears exactly where it appeared before. The
82
+ * fallback only decides what THIS node says, and a site's display name is its
83
+ * publisher's name on the overwhelming majority of sites — the two differ when
84
+ * a company's trading name differs from its site's, which is precisely the case
85
+ * the Entity form exists to express.
86
+ *
87
+ * ## `@id`
88
+ *
89
+ * Both this node and the nested `publisher` carry the same `@id` when an
90
+ * origin is known, so a consumer merges them into one entity instead of
91
+ * reading a site that names its publisher twice with different detail.
92
+ *
93
+ * Returns `undefined` only when there is no name to be had from anywhere —
94
+ * a host document with no entity, no display name and no SEO title.
95
+ */
96
+ export declare function siteEntityJsonLd(host: SiteEntityHost | null | undefined, context?: ContentAuthorImageContext): Record<string, unknown> | undefined;
@@ -0,0 +1,220 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { AUTHOR_NAME_MAX_LENGTH, SITE_ENTITY_FRAGMENT, contentAuthorSchemaType, hostSeoEntityImageJsonLd, siteProfileUrls } from "./content-authors.js";
18
+ import { PAYMENT_ACCEPTED_MAX_LENGTH, PRICE_RANGE_MAX_LENGTH, localBusinessType, normalizeAreaServed, parseOpeningHours } from "./local-business.js";
19
+ /*
20
+ * The SITE entity's JSON-LD: the nested `publisher` and the standalone node.
21
+ *
22
+ * ⛔ Kept apart from `content-authors`, which the compose pipeline reads and
23
+ * therefore ships whole in the site runtime a published page downloads
24
+ * (`check:tenant-wire-weight`). Only the tenant page's server render calls
25
+ * these, and the LocalBusiness allow-list and hours parser they carry
26
+ * (AGL-3383) have no business in a visitor's bundle. Reached through
27
+ * `@aglyn/aglyn/server` only.
28
+ */ /** `content-authors`' trim-and-cap, for the fields this module reads. */ const text = (value, max = AUTHOR_NAME_MAX_LENGTH)=>typeof value === 'string' ? value.trim().slice(0, max) : '';
29
+ /**
30
+ * The `@type` the SITE entity publishes under — {@link contentAuthorSchemaType}
31
+ * with one refinement (AGL-3383): an entity that is not a Person and names a
32
+ * business type on the allow-list publishes as that type.
33
+ *
34
+ * Person wins over a business type, because `schema.org` gives a Person none
35
+ * of the properties a business type exists to carry. The stored `type` stays
36
+ * Organization or Person either way; every LocalBusiness subtype IS an
37
+ * Organization, so a consumer reading this as one is still right.
38
+ *
39
+ * Shared by the standalone node and the nested `publisher`, which carry the
40
+ * same `@id` and must therefore not disagree about what they are.
41
+ */ export function siteEntitySchemaType(entity) {
42
+ var _localBusinessType;
43
+ const base = contentAuthorSchemaType(entity == null ? void 0 : entity.type);
44
+ if (base === 'Person') return base;
45
+ return (_localBusinessType = localBusinessType(entity == null ? void 0 : entity.businessType)) != null ? _localBusinessType : base;
46
+ }
47
+ /**
48
+ * `schema.org` JSON-LD for the SITE entity (`host.seo.entity`) — the value
49
+ * `Article.publisher` and `WebSite.publisher` are built from.
50
+ *
51
+ * Lives beside {@link contentAuthorJsonLd} on purpose (AGL-2486): a site
52
+ * entity and a post author answer the same question about two different
53
+ * subjects, and the reason the Person branch was broken for two years is that
54
+ * the answer was written inline at its one call site where nothing could
55
+ * test it. One module, two entry points, one `Person`/`Organization`
56
+ * decision — {@link contentAuthorSchemaType}.
57
+ *
58
+ * `logo` is emitted verbatim on BOTH branches, unchanged from what the tenant
59
+ * has always published. It is `host.seo.entity.logo`, a plain URL field on
60
+ * the Setup form rather than a media-picker target, and re-keying it to
61
+ * `image` for a Person would change the publisher block on every page of
62
+ * every site that set one — a separate decision from this one.
63
+ */ export function hostSeoEntityJsonLd(entity) {
64
+ const name = text(entity == null ? void 0 : entity.name, 400);
65
+ if (!name) return undefined;
66
+ return {
67
+ '@type': siteEntitySchemaType(entity),
68
+ name
69
+ };
70
+ }
71
+ /**
72
+ * The site's publisher as a STANDALONE, fully populated `schema.org` node
73
+ * (AGL-2716).
74
+ *
75
+ * ## Why this exists next to {@link hostSeoEntityJsonLd}
76
+ *
77
+ * That function builds the value `publisher` takes: a `@type` and a `name`,
78
+ * nested inside a `WebSite` or an `Article`, gated on the author having filled
79
+ * in the Entity form. It is deliberately narrow, and every call site nests it.
80
+ *
81
+ * A consumer asking "who runs this site, and how do I reach them" — an AI
82
+ * assistant answering a contact question, a readiness audit checking whether a
83
+ * business is identifiable — looks for a TOP-LEVEL node of type `Organization`
84
+ * carrying `name`, `description`, `contactPoint` and `address`. A publisher
85
+ * nested two levels inside a `WebSite` answers a different question, and a
86
+ * publisher that is absent because nobody filled in a form answers none.
87
+ *
88
+ * So this one:
89
+ *
90
+ * - is emitted as its own `<script type="application/ld+json">`, at the top
91
+ * level, where it can be found without walking into another node;
92
+ * - FALLS BACK to the site's own identity when the Entity form is empty, so
93
+ * every site on the platform publishes a complete entity rather than only
94
+ * the sites whose author found the form;
95
+ * - carries the contact and address fields, which are what turn "a name" into
96
+ * "a business a consumer can verify".
97
+ *
98
+ * ## Why the fallback is safe
99
+ *
100
+ * It changes nothing that already exists: `hostSeoEntityJsonLd` still gates on
101
+ * `entity.name`, so `publisher` appears exactly where it appeared before. The
102
+ * fallback only decides what THIS node says, and a site's display name is its
103
+ * publisher's name on the overwhelming majority of sites — the two differ when
104
+ * a company's trading name differs from its site's, which is precisely the case
105
+ * the Entity form exists to express.
106
+ *
107
+ * ## `@id`
108
+ *
109
+ * Both this node and the nested `publisher` carry the same `@id` when an
110
+ * origin is known, so a consumer merges them into one entity instead of
111
+ * reading a site that names its publisher twice with different detail.
112
+ *
113
+ * Returns `undefined` only when there is no name to be had from anywhere —
114
+ * a host document with no entity, no display name and no SEO title.
115
+ */ export function siteEntityJsonLd(host, context) {
116
+ var _ref, _ref1;
117
+ var _host_seo, _host_seo1, _context_origin, _host_seo2, _entity_address, _entity_address1, _entity_address2, _entity_address3, _entity_address4;
118
+ const entity = (_ref = host == null ? void 0 : (_host_seo = host.seo) == null ? void 0 : _host_seo.entity) != null ? _ref : undefined;
119
+ const name = text(entity == null ? void 0 : entity.name, 400) || text(host == null ? void 0 : host.displayName, 400) || text(host == null ? void 0 : (_host_seo1 = host.seo) == null ? void 0 : _host_seo1.title, 400);
120
+ if (!name) return undefined;
121
+ const origin = (_ref1 = context == null ? void 0 : (_context_origin = context.origin) == null ? void 0 : _context_origin.replace(/\/+$/, '')) != null ? _ref1 : '';
122
+ const description = text(entity == null ? void 0 : entity.description, 800) || text(host == null ? void 0 : (_host_seo2 = host.seo) == null ? void 0 : _host_seo2.description, 800);
123
+ const url = text(entity == null ? void 0 : entity.url, 800) || origin;
124
+ /*
125
+ `sameAs` is de-duplicated and https-only, matching the author serializer:
126
+ a profile URL that is not fetchable teaches a consumer nothing, and the
127
+ same profile listed twice reads as two identities.
128
+
129
+ Through `siteProfileUrls` (AGL-3148) so the site's rendered social links
130
+ count as profiles it claims, rather than only the SEO field that no host
131
+ on the platform had filled in.
132
+ */ const sameAs = siteProfileUrls(host);
133
+ /*
134
+ A contactPoint needs something to contact. `contactType` alone is a label
135
+ on nothing, so the point is emitted only when an address or a number is
136
+ present — `schema.org` requires neither, and a consumer that finds a
137
+ ContactPoint with no channel has been told less than it would learn from
138
+ its absence.
139
+ */ const email = text(entity == null ? void 0 : entity.email, 320);
140
+ const telephone = text(entity == null ? void 0 : entity.telephone, 64);
141
+ const contactType = text(entity == null ? void 0 : entity.contactType, 120) || 'customer support';
142
+ const contactPoint = email || telephone ? _extends({
143
+ '@type': 'ContactPoint',
144
+ contactType
145
+ }, email ? {
146
+ email
147
+ } : {}, telephone ? {
148
+ telephone
149
+ } : {}) : undefined;
150
+ const addressFields = {
151
+ streetAddress: text(entity == null ? void 0 : (_entity_address = entity.address) == null ? void 0 : _entity_address.streetAddress, 200),
152
+ addressLocality: text(entity == null ? void 0 : (_entity_address1 = entity.address) == null ? void 0 : _entity_address1.addressLocality, 120),
153
+ addressRegion: text(entity == null ? void 0 : (_entity_address2 = entity.address) == null ? void 0 : _entity_address2.addressRegion, 120),
154
+ postalCode: text(entity == null ? void 0 : (_entity_address3 = entity.address) == null ? void 0 : _entity_address3.postalCode, 40),
155
+ addressCountry: text(entity == null ? void 0 : (_entity_address4 = entity.address) == null ? void 0 : _entity_address4.addressCountry, 120)
156
+ };
157
+ const address = Object.entries(addressFields).filter(([, value])=>value);
158
+ const schemaType = siteEntitySchemaType(entity);
159
+ const business = schemaType !== 'Person' && schemaType !== 'Organization' ? localBusinessJsonLd(entity, telephone) : {};
160
+ return _extends({
161
+ '@context': 'https://schema.org',
162
+ '@type': schemaType
163
+ }, origin ? {
164
+ '@id': `${origin}/${SITE_ENTITY_FRAGMENT}`
165
+ } : {}, {
166
+ name
167
+ }, description ? {
168
+ description
169
+ } : {}, url ? {
170
+ url
171
+ } : {}, hostSeoEntityImageJsonLd(entity, context), sameAs.length ? {
172
+ sameAs
173
+ } : {}, contactPoint ? {
174
+ contactPoint
175
+ } : {}, address.length ? {
176
+ address: _extends({
177
+ '@type': 'PostalAddress'
178
+ }, Object.fromEntries(address))
179
+ } : {}, business);
180
+ }
181
+ /**
182
+ * The properties only a LocalBusiness carries (AGL-3383), spread after
183
+ * everything an Organization publishes so an Organization's output is
184
+ * unchanged key for key.
185
+ *
186
+ * - `telephone` on the node itself as well as on its `contactPoint`: a
187
+ * LocalBusiness has the property directly, and it is where a local result
188
+ * reads the number from.
189
+ * - `areaServed` as named `City` places. A service-area business commonly has
190
+ * no street address to publish, and this is what says where it works.
191
+ * - `openingHoursSpecification` from the hours text, invalid lines dropped.
192
+ *
193
+ * Every one of them is omitted when empty, never published as `[]` or `''`.
194
+ */ function localBusinessJsonLd(entity, telephone) {
195
+ const areaServed = normalizeAreaServed(entity == null ? void 0 : entity.areaServed);
196
+ const hours = parseOpeningHours(entity == null ? void 0 : entity.openingHours);
197
+ const priceRange = text(entity == null ? void 0 : entity.priceRange, PRICE_RANGE_MAX_LENGTH);
198
+ const paymentAccepted = text(entity == null ? void 0 : entity.paymentAccepted, PAYMENT_ACCEPTED_MAX_LENGTH);
199
+ return _extends({}, telephone ? {
200
+ telephone
201
+ } : {}, areaServed.length ? {
202
+ areaServed: areaServed.map((name)=>({
203
+ '@type': 'City',
204
+ name
205
+ }))
206
+ } : {}, hours.length ? {
207
+ openingHoursSpecification: hours.map((row)=>({
208
+ '@type': 'OpeningHoursSpecification',
209
+ dayOfWeek: row.days,
210
+ opens: row.opens,
211
+ closes: row.closes
212
+ }))
213
+ } : {}, priceRange ? {
214
+ priceRange
215
+ } : {}, paymentAccepted ? {
216
+ paymentAccepted
217
+ } : {});
218
+ }
219
+
220
+ //# sourceMappingURL=site-entity-json-ld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/site-entity-json-ld.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n type ContentAuthorImageContext,\n type HostSeoEntity,\n type SiteEntityHost,\n AUTHOR_NAME_MAX_LENGTH,\n SITE_ENTITY_FRAGMENT,\n contentAuthorSchemaType,\n hostSeoEntityImageJsonLd,\n siteProfileUrls,\n} from './content-authors'\nimport {\n type LocalBusinessType,\n PAYMENT_ACCEPTED_MAX_LENGTH,\n PRICE_RANGE_MAX_LENGTH,\n localBusinessType,\n normalizeAreaServed,\n parseOpeningHours,\n} from './local-business'\n\n/*\n * The SITE entity's JSON-LD: the nested `publisher` and the standalone node.\n *\n * ⛔ Kept apart from `content-authors`, which the compose pipeline reads and\n * therefore ships whole in the site runtime a published page downloads\n * (`check:tenant-wire-weight`). Only the tenant page's server render calls\n * these, and the LocalBusiness allow-list and hours parser they carry\n * (AGL-3383) have no business in a visitor's bundle. Reached through\n * `@aglyn/aglyn/server` only.\n */\n\n/** `content-authors`' trim-and-cap, for the fields this module reads. */\nconst text = (value: unknown, max = AUTHOR_NAME_MAX_LENGTH): string =>\n typeof value === 'string' ? value.trim().slice(0, max) : ''\n\n/**\n * The `@type` the SITE entity publishes under — {@link contentAuthorSchemaType}\n * with one refinement (AGL-3383): an entity that is not a Person and names a\n * business type on the allow-list publishes as that type.\n *\n * Person wins over a business type, because `schema.org` gives a Person none\n * of the properties a business type exists to carry. The stored `type` stays\n * Organization or Person either way; every LocalBusiness subtype IS an\n * Organization, so a consumer reading this as one is still right.\n *\n * Shared by the standalone node and the nested `publisher`, which carry the\n * same `@id` and must therefore not disagree about what they are.\n */\nexport function siteEntitySchemaType(\n entity: HostSeoEntity | null | undefined,\n): 'Person' | 'Organization' | LocalBusinessType {\n const base = contentAuthorSchemaType(entity?.type)\n if (base === 'Person') return base\n return localBusinessType(entity?.businessType) ?? base\n}\n\n/**\n * `schema.org` JSON-LD for the SITE entity (`host.seo.entity`) — the value\n * `Article.publisher` and `WebSite.publisher` are built from.\n *\n * Lives beside {@link contentAuthorJsonLd} on purpose (AGL-2486): a site\n * entity and a post author answer the same question about two different\n * subjects, and the reason the Person branch was broken for two years is that\n * the answer was written inline at its one call site where nothing could\n * test it. One module, two entry points, one `Person`/`Organization`\n * decision — {@link contentAuthorSchemaType}.\n *\n * `logo` is emitted verbatim on BOTH branches, unchanged from what the tenant\n * has always published. It is `host.seo.entity.logo`, a plain URL field on\n * the Setup form rather than a media-picker target, and re-keying it to\n * `image` for a Person would change the publisher block on every page of\n * every site that set one — a separate decision from this one.\n */\nexport function hostSeoEntityJsonLd(\n entity: HostSeoEntity | null | undefined,\n): Record<string, unknown> | undefined {\n const name = text(entity?.name, 400)\n if (!name) return undefined\n return {\n '@type': siteEntitySchemaType(entity),\n name,\n }\n}\n\n/**\n * The site's publisher as a STANDALONE, fully populated `schema.org` node\n * (AGL-2716).\n *\n * ## Why this exists next to {@link hostSeoEntityJsonLd}\n *\n * That function builds the value `publisher` takes: a `@type` and a `name`,\n * nested inside a `WebSite` or an `Article`, gated on the author having filled\n * in the Entity form. It is deliberately narrow, and every call site nests it.\n *\n * A consumer asking \"who runs this site, and how do I reach them\" — an AI\n * assistant answering a contact question, a readiness audit checking whether a\n * business is identifiable — looks for a TOP-LEVEL node of type `Organization`\n * carrying `name`, `description`, `contactPoint` and `address`. A publisher\n * nested two levels inside a `WebSite` answers a different question, and a\n * publisher that is absent because nobody filled in a form answers none.\n *\n * So this one:\n *\n * - is emitted as its own `<script type=\"application/ld+json\">`, at the top\n * level, where it can be found without walking into another node;\n * - FALLS BACK to the site's own identity when the Entity form is empty, so\n * every site on the platform publishes a complete entity rather than only\n * the sites whose author found the form;\n * - carries the contact and address fields, which are what turn \"a name\" into\n * \"a business a consumer can verify\".\n *\n * ## Why the fallback is safe\n *\n * It changes nothing that already exists: `hostSeoEntityJsonLd` still gates on\n * `entity.name`, so `publisher` appears exactly where it appeared before. The\n * fallback only decides what THIS node says, and a site's display name is its\n * publisher's name on the overwhelming majority of sites — the two differ when\n * a company's trading name differs from its site's, which is precisely the case\n * the Entity form exists to express.\n *\n * ## `@id`\n *\n * Both this node and the nested `publisher` carry the same `@id` when an\n * origin is known, so a consumer merges them into one entity instead of\n * reading a site that names its publisher twice with different detail.\n *\n * Returns `undefined` only when there is no name to be had from anywhere —\n * a host document with no entity, no display name and no SEO title.\n */\nexport function siteEntityJsonLd(\n host: SiteEntityHost | null | undefined,\n context?: ContentAuthorImageContext,\n): Record<string, unknown> | undefined {\n const entity = host?.seo?.entity ?? undefined\n const name =\n text(entity?.name, 400) ||\n text(host?.displayName, 400) ||\n text(host?.seo?.title, 400)\n if (!name) return undefined\n\n const origin = context?.origin?.replace(/\\/+$/, '') ?? ''\n const description =\n text(entity?.description, 800) || text(host?.seo?.description, 800)\n const url = text(entity?.url, 800) || origin\n\n /*\n `sameAs` is de-duplicated and https-only, matching the author serializer:\n a profile URL that is not fetchable teaches a consumer nothing, and the\n same profile listed twice reads as two identities.\n\n Through `siteProfileUrls` (AGL-3148) so the site's rendered social links\n count as profiles it claims, rather than only the SEO field that no host\n on the platform had filled in.\n */\n const sameAs = siteProfileUrls(host)\n\n /*\n A contactPoint needs something to contact. `contactType` alone is a label\n on nothing, so the point is emitted only when an address or a number is\n present — `schema.org` requires neither, and a consumer that finds a\n ContactPoint with no channel has been told less than it would learn from\n its absence.\n */\n const email = text(entity?.email, 320)\n const telephone = text(entity?.telephone, 64)\n const contactType = text(entity?.contactType, 120) || 'customer support'\n const contactPoint =\n email || telephone\n ? {\n '@type': 'ContactPoint',\n contactType,\n ...(email ? { email } : {}),\n ...(telephone ? { telephone } : {}),\n }\n : undefined\n\n const addressFields = {\n streetAddress: text(entity?.address?.streetAddress, 200),\n addressLocality: text(entity?.address?.addressLocality, 120),\n addressRegion: text(entity?.address?.addressRegion, 120),\n postalCode: text(entity?.address?.postalCode, 40),\n addressCountry: text(entity?.address?.addressCountry, 120),\n }\n const address = Object.entries(addressFields).filter(([, value]) => value)\n const schemaType = siteEntitySchemaType(entity)\n const business =\n schemaType !== 'Person' && schemaType !== 'Organization'\n ? localBusinessJsonLd(entity, telephone)\n : {}\n\n return {\n '@context': 'https://schema.org',\n '@type': schemaType,\n ...(origin ? { '@id': `${origin}/${SITE_ENTITY_FRAGMENT}` } : {}),\n name,\n ...(description ? { description } : {}),\n ...(url ? { url } : {}),\n // `logo` for an Organization, `image` for a Person — the one field whose\n // KEY the branch changes. Shared with the nested publisher so the two\n // nodes cannot describe the same entity with different pictures.\n ...hostSeoEntityImageJsonLd(entity, context),\n ...(sameAs.length ? { sameAs } : {}),\n ...(contactPoint ? { contactPoint } : {}),\n ...(address.length\n ? { address: { '@type': 'PostalAddress', ...Object.fromEntries(address) } }\n : {}),\n ...business,\n }\n}\n\n/**\n * The properties only a LocalBusiness carries (AGL-3383), spread after\n * everything an Organization publishes so an Organization's output is\n * unchanged key for key.\n *\n * - `telephone` on the node itself as well as on its `contactPoint`: a\n * LocalBusiness has the property directly, and it is where a local result\n * reads the number from.\n * - `areaServed` as named `City` places. A service-area business commonly has\n * no street address to publish, and this is what says where it works.\n * - `openingHoursSpecification` from the hours text, invalid lines dropped.\n *\n * Every one of them is omitted when empty, never published as `[]` or `''`.\n */\nfunction localBusinessJsonLd(\n entity: HostSeoEntity | null | undefined,\n telephone: string,\n): Record<string, unknown> {\n const areaServed = normalizeAreaServed(entity?.areaServed)\n const hours = parseOpeningHours(entity?.openingHours)\n const priceRange = text(entity?.priceRange, PRICE_RANGE_MAX_LENGTH)\n const paymentAccepted = text(\n entity?.paymentAccepted,\n PAYMENT_ACCEPTED_MAX_LENGTH,\n )\n return {\n ...(telephone ? { telephone } : {}),\n ...(areaServed.length\n ? { areaServed: areaServed.map((name) => ({ '@type': 'City', name })) }\n : {}),\n ...(hours.length\n ? {\n openingHoursSpecification: hours.map((row) => ({\n '@type': 'OpeningHoursSpecification',\n dayOfWeek: row.days,\n opens: row.opens,\n closes: row.closes,\n })),\n }\n : {}),\n ...(priceRange ? { priceRange } : {}),\n ...(paymentAccepted ? { paymentAccepted } : {}),\n }\n}\n"],"names":["AUTHOR_NAME_MAX_LENGTH","SITE_ENTITY_FRAGMENT","contentAuthorSchemaType","hostSeoEntityImageJsonLd","siteProfileUrls","PAYMENT_ACCEPTED_MAX_LENGTH","PRICE_RANGE_MAX_LENGTH","localBusinessType","normalizeAreaServed","parseOpeningHours","text","value","max","trim","slice","siteEntitySchemaType","entity","base","type","businessType","hostSeoEntityJsonLd","name","undefined","siteEntityJsonLd","host","context","seo","displayName","title","origin","replace","description","url","sameAs","email","telephone","contactType","contactPoint","addressFields","streetAddress","address","addressLocality","addressRegion","postalCode","addressCountry","Object","entries","filter","schemaType","business","localBusinessJsonLd","length","fromEntries","areaServed","hours","openingHours","priceRange","paymentAccepted","map","openingHoursSpecification","row","dayOfWeek","days","opens","closes"],"mappings":"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"}
@@ -294,6 +294,30 @@ export interface AglynHost extends AglynDocument {
294
294
  /** ISO 3166-1 alpha-2, or a country name. */
295
295
  addressCountry?: string;
296
296
  };
297
+ /**
298
+ * The `schema.org` LocalBusiness subtype this entity publishes as —
299
+ * `Plumber`, `Restaurant`, `LocalBusiness` (AGL-3383).
300
+ *
301
+ * Beside `type` rather than a third value of it: `type` still
302
+ * answers Organization or Person for every reader that asks, and a
303
+ * business is an Organization. Only a value on `LOCAL_BUSINESS_TYPES`
304
+ * publishes; anything else, or a Person, publishes as before. The four
305
+ * fields below are published only when this is.
306
+ */
307
+ businessType?: string;
308
+ /** Cities or areas the business serves, published as `areaServed`. */
309
+ areaServed?: string[];
310
+ /**
311
+ * Opening hours, one line per set of days — `Mo-Fr 09:00-17:00` — the
312
+ * syntax `schema.org`'s own `openingHours` text uses. Parsed into
313
+ * `openingHoursSpecification` at serialization; a line that does not
314
+ * read is dropped. See `parseOpeningHours`.
315
+ */
316
+ openingHours?: string;
317
+ /** Free text, e.g. `$$` or `$100–$500`. */
318
+ priceRange?: string;
319
+ /** Free text, e.g. `Cash, credit card, Zelle`. */
320
+ paymentAccepted?: string;
297
321
  };
298
322
  /**
299
323
  * Guidance for AI agents, published in `/llms.txt` (AGL-2716).