@aglyn/plugins-ai 1.0.0-beta.270 → 1.0.0-beta.271
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 +17 -17
- package/src/lib/activity/ai-activity-actions.d.ts +1 -1
- package/src/lib/activity/ai-activity-actions.js +1 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/components/ai-credits-card.component.d.ts +4 -3
- package/src/lib/components/ai-credits-card.component.js +15 -15
- package/src/lib/components/ai-credits-card.component.js.map +1 -1
- package/src/lib/components/ai-job-input-form.component.d.ts +36 -0
- package/src/lib/components/ai-job-input-form.component.js +100 -0
- package/src/lib/components/ai-job-input-form.component.js.map +1 -0
- package/src/lib/components/ai-job-requests.d.ts +4 -0
- package/src/lib/components/ai-job-requests.js +4 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +99 -31
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +43 -2
- package/src/lib/components/ai-site-start-steps.component.js +651 -15
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -1
- package/src/lib/components/ai-site-start-tracking.d.ts +10 -0
- package/src/lib/components/ai-site-start-tracking.js +14 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -1
- package/src/lib/components/ai-site-style-previews.js +26 -22
- package/src/lib/components/ai-site-style-previews.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +7 -5
- package/src/lib/components/ai-upsell-dialog.component.js +8 -6
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -1
- package/src/lib/components/assist-build-card.component.js +11 -1
- package/src/lib/components/assist-build-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +9 -1
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +8 -4
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +4 -7
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/declarations.d.ts +2 -2
- package/src/lib/declarations.js +9 -5
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.js +3 -1
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +14 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +5 -3
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-form-step.js +75 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +13 -0
- package/src/lib/jobs/ai-job-page-language.js +37 -1
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +17 -0
- package/src/lib/jobs/ai-job-page-step.js +37 -16
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +25 -2
- package/src/lib/jobs/ai-job-plan-step.js +103 -29
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.js +4 -3
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +27 -0
- package/src/lib/jobs/ai-job-site-datasets.js +140 -14
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-look.js +18 -5
- package/src/lib/jobs/ai-job-site-look.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +16 -1
- package/src/lib/jobs/ai-job-site-step.js +95 -22
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +8 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +14 -3
- package/src/lib/jobs/ai-job-tracking-step.js +28 -5
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +8 -1
- package/src/lib/jobs/ai-jobs.js +63 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +27 -2
- package/src/lib/jobs/ai-layout-stock-photos.js +190 -19
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-inspiration-signals.d.ts +98 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js +585 -0
- package/src/lib/jobs/ai-site-inspiration-signals.js.map +1 -0
- package/src/lib/jobs/ai-site-inspiration.d.ts +157 -0
- package/src/lib/jobs/ai-site-inspiration.js +346 -0
- package/src/lib/jobs/ai-site-inspiration.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +26 -1
- package/src/lib/jobs/ai-site-publish.js +162 -13
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +16 -0
- package/src/lib/jobs/ai-theme-brand-inputs.js +18 -4
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.d.ts +97 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js +137 -0
- package/src/lib/jobs/fixtures/ai-candle-shop-free-plan-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.d.ts +38 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js +103 -0
- package/src/lib/jobs/fixtures/ai-site-inspiration-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.d.ts +25 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js +174 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3988-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +1 -1
- package/src/lib/layout-language/ai-layout-compiler.js +438 -25
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-contact.d.ts +57 -0
- package/src/lib/layout-language/ai-layout-contact.js +137 -0
- package/src/lib/layout-language/ai-layout-contact.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +12 -0
- package/src/lib/layout-language/ai-layout-copy.js +34 -2
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +65 -8
- package/src/lib/layout-language/ai-layout-design.js +411 -97
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.js +23 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
- package/src/lib/layout-language/ai-layout-labels.js +12 -6
- package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +3 -2
- package/src/lib/layout-language/ai-layout-language.js +4 -3
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/layout-language/ai-layout-links.d.ts +13 -0
- package/src/lib/layout-language/ai-layout-links.js +30 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +49 -6
- package/src/lib/layout-language/ai-layout-pictures.js +145 -22
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +16 -1
- package/src/lib/model/ai-build-job.js +40 -0
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-job-input.d.ts +33 -0
- package/src/lib/model/ai-job-input.js +59 -0
- package/src/lib/model/ai-job-input.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +4 -0
- package/src/lib/model/ai-job-notice.js +11 -1
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +40 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-details.d.ts +122 -0
- package/src/lib/model/ai-site-details.js +300 -0
- package/src/lib/model/ai-site-details.js.map +1 -0
- package/src/lib/model/ai-site-entity.d.ts +181 -0
- package/src/lib/model/ai-site-entity.js +390 -0
- package/src/lib/model/ai-site-entity.js.map +1 -0
- package/src/lib/model/ai-site-inspiration.d.ts +72 -0
- package/src/lib/model/ai-site-inspiration.js +109 -0
- package/src/lib/model/ai-site-inspiration.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +13 -1
- package/src/lib/model/ai-site-job.js +15 -2
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +109 -21
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-look.d.ts +10 -0
- package/src/lib/model/ai-site-look.js +24 -0
- package/src/lib/model/ai-site-look.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +19 -5
- package/src/lib/model/ai-site-start-seo.js +25 -7
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start-steps.d.ts +9 -3
- package/src/lib/model/ai-site-start-steps.js +67 -5
- package/src/lib/model/ai-site-start-steps.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +68 -4
- package/src/lib/model/ai-site-start.js +166 -13
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-upsell-copy.js +4 -1
- package/src/lib/model/ai-upsell-copy.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +4 -3
- package/src/lib/plan-entitlements.js +3 -8
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +8 -1
- package/src/lib/runtime/ai-dataset-generation.js +12 -2
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +26 -33
- package/src/lib/runtime/ai-doctrine-validators.js +54 -44
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +1 -1
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +7 -0
- package/src/lib/runtime/ai-node-tree.js +9 -1
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/server/ai-create-route.d.ts +1 -1
- package/src/lib/server/ai-create-route.js +3 -4
- package/src/lib/server/ai-create-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +27 -3
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +7 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +4 -4
- package/src/lib/usage/assist-credits.js +4 -4
- package/src/lib/usage/assist-credits.js.map +1 -1
- package/src/lib/usage/staff-org-ai-view.js +6 -3
- package/src/lib/usage/staff-org-ai-view.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +4 -2
- package/src/lib/usage/staff-org-ai.js +7 -7
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage/staff-org-usage-ai.js +2 -2
- package/src/lib/usage/staff-org-usage-ai.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-publish.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 { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n blockingRouteOwner,\n normalizeScreenSlug,\n SCREEN_ROOT_PATH,\n screenRoutePathToUrl,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { resolvePageLocale, siteLanguageCode } from '@aglyn/aglyn/app-utils/seo-locale'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { sendGa4SitePublished } from '@aglyn/tenant-data-admin/server/ga4-measurement-protocol'\nimport { FieldValue, type Firestore } from 'firebase-admin/firestore'\nimport {\n aiLayoutFrameHollows,\n aiLayoutFrameHollowsSaid,\n aiLayoutFrameIsHollow,\n aiLayoutRepairFrameHollows,\n aiLayoutWithoutFrameHollows,\n} from '../layout-language/ai-layout-frame-hollows'\nimport type { AiJob, AiJobOutput, AiJobSitePublish } from '../model/ai-jobs.types'\nimport { AI_SITE_BLOG_SLUGS } from '../model/ai-site-job'\nimport { aiLayoutWithStoreLinks, type AiStoreLayoutLinks } from '../model/ai-layout-store-links'\nimport { aiLayoutWithMenuChanges, aiNoMenuChanges } from '../model/ai-layout-menus'\nimport { AI_STORE_LINKS_SOURCE_FIELD as STORE_LINKS_SOURCE_FIELD } from './ai-job-store-links-layout'\n\n/**\n * A store's links a build drafted into the site's own layout (AGL-3676): the\n * draft version, the links in it, and the site's page addresses they were\n * checked against. Its publish makes that version the live one, or — where\n * the layout changed since the draft was made — adds the same links to the\n * version that is live now.\n */\nexport interface AiSitePublishStoreLinks {\n layoutId: string\n versionId: string\n links: AiStoreLayoutLinks\n screenPaths?: Readonly<Record<string, string>>\n}\n\n/**\n * A guided site start publishes what it built (AGL-3596).\n *\n * A person who asked for \"a website\" in the guided start and watched it build\n * was then handed drafts: a site whose address still answered with the\n * starter, or with nothing. A `site` job started from the guided start (its\n * plan confirmed on the person's behalf, `inputs.autoConfirm`) therefore ends\n * by putting every page it built on the site, through the same writes a\n * person's Publish makes:\n *\n * - each page's address in the host's routing map, `publishedAt` on the page,\n * in ONE batch with the publish outbox entry that drops the live site's\n * cached pages (AGL-2575), the drop itself attempted at once;\n * - the home page takes `/` from the starter home page the site was created\n * with (AGL-3408), and an untouched starter goes to the trash rather than\n * staying beside it as a second \"Home\";\n * - the navigation entries the pages proposed are added to the header of\n * the layout they render inside, as a new live version of that layout;\n * - `site_published` once, server-side, as the scheduled publish sends it.\n *\n * A page that cannot be published — deleted meanwhile, no address, an address\n * another page holds — stays a draft and is reported with the plain sentence\n * for why. It never fails the job: the site is still built, and the person\n * fixes that one page in Pages. Every other job kind keeps producing drafts.\n *\n * The allowance is respected by construction: a draft is counted against the\n * plan's pages when it is written (`aiDraftBandRefusal`), so publishing\n * drafts adds none, and retiring the starter frees the one it held.\n */\n\nexport interface AiSitePublishDeps {\n sendSitePublished?: typeof sendGa4SitePublished\n dropCache?: typeof dropPluginSiteCache\n}\n\n/**\n * Whether a build puts its pages live (AGL-3616): only where its request\n * asked to (`publish`, set by Assist's proposal) AND the member ticked the\n * plan card's box when confirming (`publishConfirmed`, set by the resume\n * door). Every other build leaves drafts.\n */\nexport function aiJobPublishesBuild(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'build' && job.inputs?.['publish'] === true && job.inputs?.['publishConfirmed'] === true\n}\n\n/**\n * The host fields a guided start writes for its plan's language (AGL-3750),\n * as the Languages card saves one language: `locales` of that one and\n * `defaultLocale` the same. `null` — nothing written — where the plan said no\n * valid language, where it is the language the site already publishes in\n * (English for a new site), and on a site with more than one language, whose\n * languages are the owner's decision. Nothing is translated: the pages were\n * written in the brief's language, and this says so to the reader.\n */\nexport function aiSiteLanguageUpdates(\n host: { locales?: unknown; defaultLocale?: unknown },\n language: string | null | undefined,\n): { locales: string[]; defaultLocale: string } | null {\n const code = siteLanguageCode(language)\n if (!code) return null\n const locales = Array.isArray(host.locales)\n ? host.locales.filter((one): one is string => typeof one === 'string' && one.trim() !== '')\n : []\n if (locales.length > 1) return null\n const current = resolvePageLocale({\n host: {\n ...(typeof host.defaultLocale === 'string' ? { defaultLocale: host.defaultLocale } : {}),\n locales,\n },\n })\n if (current === code) return null\n return { locales: [code], defaultLocale: code }\n}\n\n/** Whether a finished job of this shape publishes what it built. */\nexport function aiJobPublishesSite(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && job.inputs?.['autoConfirm'] === true\n}\n\n/**\n * Where a site answers: its custom domain, else its subdomain under the\n * deployment's tenant apex — never a hard-coded platform host, so a\n * self-hosted deployment links to its own sites.\n */\nexport function aiSiteLiveUrl(host: { cname?: unknown; subdomain?: unknown }): string | null {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const origin = hostPublicOrigin({ cname: text(host.cname) || null, subdomain: text(host.subdomain) || null })\n return origin ? `${origin}/` : null\n}\n\n/** The sentences a page that stayed a draft is reported with. */\nexport const AI_SITE_PUBLISH_DELETED = 'It was deleted before it could be published.'\nexport const AI_SITE_PUBLISH_NO_ADDRESS = 'It has no address yet. Give it one in Pages and publish it.'\nexport const aiSitePublishTakenCopy = (path: string): string =>\n `Another page is already published at ${screenRoutePathToUrl(path)}. Give this one another address in Pages and publish it.`\nexport const AI_SITE_PUBLISH_FAILED = 'It could not be published just now. Publish it from Pages.'\n\n/** A navigation entry a page proposed, as the page step reports it. */\ninterface NavEntry {\n screenId: string\n label: string\n}\n\nfunction navEntriesOf(outputs: readonly AiJobOutput[]): NavEntry[] {\n const entries: NavEntry[] = []\n for (const output of outputs) {\n if (output.resource !== 'screen') continue\n const navigation = output.proposal?.['navigation'] as { label?: unknown } | undefined\n const label = typeof navigation?.label === 'string' ? navigation.label.trim() : ''\n if (label && !entries.some((entry) => entry.screenId === output.id)) {\n entries.push({ screenId: output.id, label })\n }\n }\n return entries\n}\n\ntype MutableNode = {\n $id: string\n componentId?: string\n parentId?: string\n props?: Record<string, unknown>\n nodes?: string[]\n [key: string]: unknown\n}\n\n/**\n * The blog paths a start's layout links and must not publish (AGL-3660): its\n * header, phone menu and footer link the blog by path while the posts part is\n * still owed, so a start whose posts part then failed or was skipped drops\n * every address the blog could have taken. None while the blog was written.\n */\nexport function aiSiteUnwrittenBlogHrefs(blogUnwritten: boolean | undefined): string[] {\n return blogUnwritten ? AI_SITE_BLOG_SLUGS.map((slug) => `/${slug}`) : []\n}\n\n/**\n * The layout's nodes with its links to the retired starter home pointed at\n * the new home, its links to pages the start did not write taken out, and a\n * link for each proposed entry the header does not already carry, added by\n * the navigation op's `aiLayoutWithMenuChanges` (AGL-3747): to the header's\n * menu and its phone menu, after their items, or after the brand in a\n * starter's Toolbar. `null` when nothing changes, or when the layout has no\n * header to hold an entry and no link to repoint or take out.\n */\nexport function aiLayoutWithNavigation(\n nodes: NodesMap,\n input: {\n entries: readonly NavEntry[]\n retiredHomeId?: string | null\n homeId?: string | null\n /** Paths whose links come out: a blog the start linked and never wrote (AGL-3660). */\n droppedHrefs?: readonly string[]\n },\n): NodesMap | null {\n const map: Record<string, MutableNode> = {}\n for (const [id, node] of Object.entries(nodes as unknown as Record<string, MutableNode>)) {\n map[id] = { ...node, ...(node.props ? { props: { ...node.props } } : {}), ...(node.nodes ? { nodes: [...node.nodes] } : {}) }\n }\n let changed = false\n // The header, the phone menu and the footer link a blog by its path before\n // its posts are written (AGL-3660); a start whose posts were not written\n // publishes none of those links to a page that does not exist.\n const dropped = new Set(input.droppedHrefs ?? [])\n if (dropped.size) {\n const gone = new Set(\n Object.entries(map)\n .filter(([, node]) => node.componentId === 'muiScreenLink' && dropped.has(String(node.props?.['href'] ?? '')))\n .map(([id]) => id),\n )\n if (gone.size) {\n for (const id of gone) delete map[id]\n for (const node of Object.values(map)) {\n if (node.nodes?.some((child) => gone.has(child))) node.nodes = node.nodes.filter((child) => !gone.has(child))\n }\n // A footer heading whose links all went goes with them, and its column\n // when that leaves it only the heading (AGL-3735).\n aiLayoutRepairFrameHollows(map as unknown as Record<string, unknown>)\n changed = true\n }\n }\n const links = Object.values(map).filter((node) => node.componentId === 'muiScreenLink')\n if (input.retiredHomeId && input.homeId) {\n for (const link of links) {\n if (link.props?.['screenId'] === input.retiredHomeId) {\n link.props = { ...link.props, screenId: input.homeId }\n changed = true\n }\n }\n }\n // The entries go into the header through the navigation op's own code\n // (AGL-3747): the bar's menu and the phone menu's, after the items already\n // there, each looking like its neighbours. A page some link already opens,\n // or an item of the same name, is not added again.\n const linked = new Set(links.map((link) => link.props?.['screenId']).filter(Boolean))\n const missing = input.entries.filter((entry) => !linked.has(entry.screenId))\n if (missing.length) {\n const added = aiLayoutWithMenuChanges(\n map,\n ['header'],\n { ...aiNoMenuChanges(), add: missing.map((entry) => ({ parent: null, label: entry.label, to: { screenId: entry.screenId } })) },\n { newId: () => `ai_nav_${createResourceUid()}` },\n )\n if (added.nodes) return added.nodes as unknown as NodesMap\n }\n return changed ? (map as unknown as NodesMap) : null\n}\n\n/** The blog a start writes, as its header entry links it (`aiSiteBlogNavPage`). */\nexport interface AiSiteBlogEntry {\n label: string\n href: string\n}\n\n/**\n * The layout's nodes with the blog's header entry put back (AGL-3747): a\n * start whose posts part failed published with every link to the blog taken\n * out (AGL-3660), and Try again's posts went live with none to reach them.\n * Added by the navigation op's `aiLayoutWithMenuChanges`, as the start's\n * other entries are — the bar's menu and the phone menu — second after the\n * home page's item, as the start first linked it (AGL-3676), else last.\n * `null` where some link already opens the blog, or the layout has no header\n * menu to hold it.\n */\nexport function aiLayoutWithBlogEntry(\n nodes: NodesMap,\n input: { blog: AiSiteBlogEntry; homeId?: string | null },\n): NodesMap | null {\n const blogHrefs = new Set([input.blog.href, ...AI_SITE_BLOG_SLUGS.map((slug) => `/${slug}`)])\n const linked = Object.values(nodes as unknown as Record<string, MutableNode>).some(\n (node) => node?.componentId === 'muiScreenLink' && blogHrefs.has(String(node.props?.['href'] ?? '')),\n )\n if (linked) return null\n const home = [...(input.homeId ? [{ screenId: input.homeId }] : []), { href: '/' }]\n const added = aiLayoutWithMenuChanges(\n nodes as unknown as Record<string, unknown>,\n ['header'],\n { ...aiNoMenuChanges(), add: [{ parent: null, label: input.blog.label, to: { href: input.blog.href }, after: home }] },\n { newId: () => `ai_nav_${createResourceUid()}` },\n )\n return (added.nodes as unknown as NodesMap | null) ?? null\n}\n\n/**\n * Puts the blog's header entry back on a live start whose posts Try again\n * wrote (AGL-3747), as a new live version of the layout its published pages\n * render inside, in one batch with the publish outbox entry that drops the\n * live site's cached pages, the drop itself attempted at once. Returns\n * whether the entry went live; never throws for a layout it cannot change.\n */\nexport async function aiRestoreGuidedBlogEntry(\n firestore: Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'hostId'>\n /** The start's published pages: the layout they share is the one changed. */\n pageIds: readonly string[]\n /** The published home page, which the blog's entry follows. */\n homeId?: string | null\n blog: AiSiteBlogEntry\n now: Date\n },\n deps: Pick<AiSitePublishDeps, 'dropCache'> = {},\n): Promise<boolean> {\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const hostId = input.job.hostId ?? ''\n if (!hostId || !input.pageIds.length) return false\n const layoutId = await aiPagesLayoutId(firestore, hostId, input.pageIds)\n if (!layoutId) return false\n const write = await aiLayoutVersionWrite(firestore, {\n hostId,\n layoutId,\n transform: (nodes) => aiLayoutWithBlogEntry(nodes, { blog: input.blog, homeId: input.homeId ?? null }),\n storeLinks: null,\n jobId: input.job.$id,\n now: input.now,\n displayName: 'Navigation for the blog',\n })\n if (!write?.version) return false\n const batch = firestore.batch()\n batch.create(write.versionRef, write.version)\n batch.set(write.layoutRef, { versionId: write.versionRef.id, updatedAt: input.now }, { merge: true })\n // A layout change reaches every page, so the announce asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n entireHost: true,\n })\n await batch.commit()\n const dropped = await dropCache({ hostIds: [hostId], reason: 'guided AI site start linked its blog' })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n return true\n}\n\n/**\n * A retry's publish merged into the start's first (AGL-3730): the pages it\n * put live join the published list, a page it published leaves the drafts,\n * and a page it left a draft is reported with its newest reason.\n */\nexport function aiMergeSitePublish(previous: AiJobSitePublish, retried: AiJobSitePublish): AiJobSitePublish {\n const now = new Set([...retried.published, ...retried.drafts].map((page) => page.id))\n return {\n liveUrl: retried.liveUrl ?? previous.liveUrl,\n published: [...previous.published.filter((page) => !now.has(page.id)), ...retried.published],\n drafts: [...previous.drafts.filter((page) => !now.has(page.id)), ...retried.drafts],\n }\n}\n\n/**\n * Publishes the pages a guided site start built. Never throws for a page; a\n * Firestore failure of the publish write itself reports every page as a\n * draft, for the same reason, and is logged.\n */\nexport async function aiPublishGuidedSite(\n firestore: Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'orgId' | 'hostId'>\n outputs: readonly AiJobOutput[]\n now: Date\n /** The start owed its blog's posts and wrote none (AGL-3660): its links to the blog come out. */\n blogUnwritten?: boolean\n /** Paths the layout links that the start did not write, such as a store page (AGL-3676): their links come out. */\n unwrittenHrefs?: readonly string[]\n /** A store's links a build drafted into the site's own layout (AGL-3676), which go live with its pages. */\n storeLinks?: AiSitePublishStoreLinks | null\n /**\n * The start published once already, and Try again rebuilt what failed\n * (AGL-3730): only `outputs` go live, beside what is. The home address is\n * never taken from the starter here, the starter is never retired, and\n * `site_published` is not sent again; a retried page at `/` that the\n * starter still holds stays a draft and says so.\n */\n retry?: boolean\n /**\n * The language the plan says the brief and copy are in (AGL-3750): the\n * site publishes in it, where `aiSiteLanguageUpdates` says so.\n */\n language?: string | null\n },\n deps: AiSitePublishDeps = {},\n): Promise<AiJobSitePublish> {\n const sendSitePublished = deps.sendSitePublished ?? sendGa4SitePublished\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const { job, now } = input\n const hostId = job.hostId ?? ''\n const pages = input.outputs.filter(\n (output, index, all) =>\n output.resource === 'screen' &&\n (output.hostId ?? hostId) === hostId &&\n all.findIndex((other) => other.resource === 'screen' && other.id === output.id) === index,\n )\n const hostRef = firestore.collection('hosts').doc(hostId)\n const host = await hostRef.get()\n const result: AiJobSitePublish = {\n liveUrl: aiSiteLiveUrl({ cname: host.get('cname'), subdomain: host.get('subdomain') }),\n published: [],\n drafts: [],\n }\n if (!host.exists || !pages.length) return result\n\n const before = { ...((host.get('screens') ?? {}) as Record<string, string>) }\n const starter = (host.get('defaultHomeScreenId') as string | undefined) || null\n // A retry never takes `/` from the starter (AGL-3730): it holds it like any page.\n const placeholder = input.retry ? null : starter\n const routing = { ...before }\n const entries: Record<string, string> = {}\n const accepted: Array<{ id: string; label: string; path: string; slug: string }> = []\n for (const page of pages) {\n const snapshot = await hostRef.collection('screens').doc(page.id).get()\n const label = String(snapshot.get('displayName') ?? page.label ?? '') || page.label\n if (!snapshot.exists || snapshot.get('deletedAt') != null) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_DELETED })\n continue\n }\n const slug = normalizeScreenSlug(String(snapshot.get('slug') ?? ''))\n if (!slug) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_NO_ADDRESS })\n continue\n }\n const owner = blockingRouteOwner(routing, slug, placeholder)\n if (owner && owner !== page.id) {\n result.drafts.push({ id: page.id, label, reason: aiSitePublishTakenCopy(slug) })\n continue\n }\n routing[page.id] = slug\n entries[page.id] = slug\n accepted.push({ id: page.id, label, path: slug, slug })\n }\n if (!accepted.length) return result\n\n // The first real home page replaces the placeholder in the same write.\n const home = accepted.find((page) => page.path === SCREEN_ROOT_PATH) ?? null\n const releases =\n home && placeholder && placeholder !== home.id && before[placeholder] === SCREEN_ROOT_PATH\n ? placeholder\n : null\n // An untouched starter home (still its one first version) is retired to the\n // trash; one its owner edited stays in Pages as their draft.\n const retire = releases\n ? (await hostRef.collection('screens').doc(releases).collection('versions').limit(2).get()).size <= 1\n : false\n\n // The header of the layout the pages render inside learns their entries.\n const layoutWrite = await aiNavigationLayoutWrite(firestore, {\n hostId,\n pageIds: accepted.map((page) => page.id),\n entries: navEntriesOf(pages).filter((entry) => entries[entry.screenId]),\n droppedHrefs: [...aiSiteUnwrittenBlogHrefs(input.blogUnwritten), ...(input.unwrittenHrefs ?? [])],\n retiredHomeId: releases,\n homeId: home?.id ?? null,\n jobId: job.$id,\n now,\n storeLinks: input.storeLinks ?? null,\n }).catch((error: unknown) => {\n console.error('ai site publish: navigation not applied', { hostId, jobId: job.$id, error })\n return null\n })\n // A store's links drafted into a layout the pages' navigation did not touch go live on their own.\n const storeLinks = input.storeLinks ?? null\n const storeLinksWrite =\n storeLinks && layoutWrite?.layoutRef.id !== storeLinks.layoutId\n ? await aiLayoutVersionWrite(firestore, {\n hostId,\n layoutId: storeLinks.layoutId,\n transform: () => null,\n storeLinks,\n jobId: job.$id,\n now,\n }).catch((error: unknown) => {\n console.error('ai site publish: store links not applied', { hostId, jobId: job.$id, error })\n return null\n })\n : null\n const layoutWrites = [layoutWrite, storeLinksWrite].filter((write): write is LayoutWrite => Boolean(write))\n\n const paths = [\n ...new Set(\n [\n ...Object.values(entries),\n ...(releases ? [SCREEN_ROOT_PATH] : []),\n ].map((path) => screenRoutePathToUrl(path)),\n ),\n ]\n const batch = firestore.batch()\n const hostUpdates: Record<string, unknown> = { updatedAt: now }\n for (const [id, path] of Object.entries(entries)) hostUpdates[`screens.${id}`] = path\n if (releases) {\n hostUpdates[`screens.${releases}`] = FieldValue.delete()\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n batch.set(\n hostRef.collection('screens').doc(releases),\n { publishedAt: FieldValue.delete(), ...(retire ? { deletedAt: now } : {}) },\n { merge: true },\n )\n } else if (placeholder && entries[placeholder]) {\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n }\n // The site's language, from its plan (AGL-3750): every page's `<html lang>`\n // reads it, so the announce below asks for the whole site.\n const language = aiSiteLanguageUpdates(\n { locales: host.get('locales'), defaultLocale: host.get('defaultLocale') },\n input.language,\n )\n if (language) Object.assign(hostUpdates, language)\n const entireHost = layoutWrites.length > 0 || language !== null\n batch.update(hostRef, hostUpdates)\n for (const page of accepted) {\n batch.set(hostRef.collection('screens').doc(page.id), { slug: page.slug, publishedAt: now }, { merge: true })\n }\n for (const write of layoutWrites) {\n if (write.version) batch.create(write.versionRef, write.version)\n batch.set(write.layoutRef, { versionId: write.versionRef.id, updatedAt: now }, { merge: true })\n }\n // The announce, written down in the publish's own batch (AGL-2575): a\n // layout change reaches every page, so it asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: paths.length ? paths : [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n ...(entireHost ? { entireHost: true } : {}),\n })\n try {\n await batch.commit()\n } catch (error) {\n console.error('ai site publish failed', { hostId, jobId: job.$id, error })\n for (const page of accepted) result.drafts.push({ id: page.id, label: page.label, reason: AI_SITE_PUBLISH_FAILED })\n return result\n }\n result.published = accepted.map(({ id, label, path }) => ({ id, label, path: screenRoutePathToUrl(path) }))\n\n // The live site's cached pages go now; the outbox entry stays for the\n // drain unless the drop certainly happened.\n const dropped = await dropCache({\n hostIds: [hostId],\n reason: 'guided AI site start published',\n ...(entireHost ? {} : { paths: { [hostId]: paths } }),\n })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n // A site that came alive with nobody's browser on the publish (AGL-1589),\n // announced once: a retry's pages join a site already announced (AGL-3730).\n if (!input.retry) await sendSitePublished({ hostId, firstPublish: isFirstPublishedRoute(before, placeholder) })\n return result\n}\n\ninterface LayoutWrite {\n layoutRef: FirebaseFirestore.DocumentReference\n versionRef: FirebaseFirestore.DocumentReference\n /** The new version to create; `null` where the live version becomes one already written (a store's links draft). */\n version: Record<string, unknown> | null\n}\n\n/**\n * The new live version of the layout the published pages render inside, with\n * their navigation entries and the home link moved off a retired starter;\n * `null` when there is nothing to change or no one layout to change.\n */\nasync function aiNavigationLayoutWrite(\n firestore: Firestore,\n input: {\n hostId: string\n pageIds: readonly string[]\n entries: readonly NavEntry[]\n droppedHrefs: readonly string[]\n retiredHomeId: string | null\n homeId: string | null\n jobId: string\n now: Date\n storeLinks?: AiSitePublishStoreLinks | null\n },\n): Promise<LayoutWrite | null> {\n if (!input.entries.length && !input.retiredHomeId && !input.droppedHrefs.length && !input.storeLinks) return null\n const layoutId = await aiPagesLayoutId(firestore, input.hostId, input.pageIds)\n if (!layoutId) return null\n return aiLayoutVersionWrite(firestore, {\n hostId: input.hostId,\n layoutId,\n transform: (nodes) => aiLayoutWithNavigation(nodes, input),\n storeLinks: input.storeLinks ?? null,\n jobId: input.jobId,\n now: input.now,\n })\n}\n\n/** The one layout the pages render inside, by their current versions; `null` where they do not share one. */\nasync function aiPagesLayoutId(firestore: Firestore, hostId: string, pageIds: readonly string[]): Promise<string | null> {\n const hostRef = firestore.collection('hosts').doc(hostId)\n const layoutIds = new Set<string>()\n for (const id of pageIds) {\n const screen = await hostRef.collection('screens').doc(id).get()\n const versionId = screen.get('versionId')\n const version =\n typeof versionId === 'string' && versionId\n ? await hostRef.collection('screens').doc(id).collection('versions').doc(versionId).get()\n : null\n const layoutId = version?.get('layoutId') ?? screen.get('layoutId')\n if (typeof layoutId === 'string' && layoutId) layoutIds.add(layoutId)\n }\n return layoutIds.size === 1 ? [...layoutIds][0] : null\n}\n\n/**\n * The new live version of one layout: `transform` applied to the version\n * live now. A store's links drafted into this layout (AGL-3676) start it from\n * that draft while the draft was made from the live version, and are added\n * to the live version otherwise; a draft with nothing more to add becomes\n * the live version as it is. `null` when nothing changes.\n */\nasync function aiLayoutVersionWrite(\n firestore: Firestore,\n input: {\n hostId: string\n layoutId: string\n transform: (nodes: NodesMap) => NodesMap | null\n storeLinks: AiSitePublishStoreLinks | null\n jobId: string\n now: Date\n /** The new version's name in the layout's versions. */\n displayName?: string\n },\n): Promise<LayoutWrite | null> {\n const { layoutId } = input\n const layoutRef = firestore.collection('hosts').doc(input.hostId).collection('layouts').doc(layoutId)\n const layout = await layoutRef.get()\n const currentVersionId = layout.get('versionId')\n if (!layout.exists || layout.get('deletedAt') != null || typeof currentVersionId !== 'string') return null\n const current = await layoutRef.collection('versions').doc(currentVersionId).get()\n const links = input.storeLinks?.layoutId === layoutId ? input.storeLinks : null\n const draftRef = links ? layoutRef.collection('versions').doc(links.versionId) : null\n const draft = draftRef ? await draftRef.get() : null\n const fromDraft = Boolean(draft?.exists && draft.get(STORE_LINKS_SOURCE_FIELD) === currentVersionId)\n const base = fromDraft && draft ? draft : current\n const nodes = base.exists ? decodeStoredNodes<NodesMap>(base.get('nodes')) : null\n if (!nodes) return null\n let next = input.transform(nodes)\n if (links && !fromDraft) {\n const added = aiLayoutWithStoreLinks((next ?? nodes) as never, links.links, links.screenPaths)\n next = (added.nodes as NodesMap | null) ?? next\n }\n // What a publish puts live has no hollow heading, column or menu (AGL-3735).\n next = (aiLayoutWithoutFrameHollows((next ?? nodes) as unknown as Record<string, unknown>) as NodesMap | null) ?? next\n const hollow = aiLayoutFrameHollows((next ?? nodes) as unknown as Record<string, unknown>)\n if (aiLayoutFrameIsHollow(hollow)) {\n console.error('ai site publish: a layout is still hollow after its repair', { layoutId, hollow: aiLayoutFrameHollowsSaid(hollow) })\n }\n if (!next) return fromDraft && draftRef ? { layoutRef, versionRef: draftRef, version: null } : null\n const packed = encodeStoredNodes(next)\n if (!packed) return null\n const versionRef = layoutRef.collection('versions').doc(createResourceUid())\n return {\n layoutRef,\n versionRef,\n version: {\n layoutId,\n hostId: input.hostId,\n displayName: input.displayName ?? 'Navigation for the new pages',\n nodes: Buffer.from(packed),\n createdAt: input.now,\n updatedAt: input.now,\n aiJobId: input.jobId,\n },\n }\n}\n"],"names":["isFirstPublishedRoute","createResourceUid","hostPublicOrigin","blockingRouteOwner","normalizeScreenSlug","SCREEN_ROOT_PATH","screenRoutePathToUrl","resolvePageLocale","siteLanguageCode","decodeStoredNodes","encodeStoredNodes","dropPluginSiteCache","sendGa4SitePublished","FieldValue","aiLayoutFrameHollows","aiLayoutFrameHollowsSaid","aiLayoutFrameIsHollow","aiLayoutRepairFrameHollows","aiLayoutWithoutFrameHollows","AI_SITE_BLOG_SLUGS","aiLayoutWithStoreLinks","aiLayoutWithMenuChanges","aiNoMenuChanges","AI_STORE_LINKS_SOURCE_FIELD","STORE_LINKS_SOURCE_FIELD","aiJobPublishesBuild","job","kind","inputs","aiSiteLanguageUpdates","host","language","code","locales","Array","isArray","filter","one","trim","length","current","defaultLocale","aiJobPublishesSite","aiSiteLiveUrl","text","value","origin","cname","subdomain","AI_SITE_PUBLISH_DELETED","AI_SITE_PUBLISH_NO_ADDRESS","aiSitePublishTakenCopy","path","AI_SITE_PUBLISH_FAILED","navEntriesOf","outputs","entries","output","resource","navigation","proposal","label","some","entry","screenId","id","push","aiSiteUnwrittenBlogHrefs","blogUnwritten","map","slug","aiLayoutWithNavigation","nodes","input","node","Object","props","changed","dropped","Set","droppedHrefs","size","gone","componentId","has","String","values","child","links","retiredHomeId","homeId","link","linked","Boolean","missing","added","add","parent","to","newId","aiLayoutWithBlogEntry","blogHrefs","blog","href","home","after","aiRestoreGuidedBlogEntry","firestore","deps","dropCache","hostId","pageIds","layoutId","aiPagesLayoutId","write","aiLayoutVersionWrite","transform","storeLinks","jobId","$id","now","displayName","version","batch","create","versionRef","set","layoutRef","versionId","updatedAt","merge","outboxRef","collection","doc","paths","createdAt","serverTimestamp","attempts","entireHost","commit","hostIds","reason","complete","delete","catch","undefined","aiMergeSitePublish","previous","retried","published","drafts","page","liveUrl","aiPublishGuidedSite","accepted","sendSitePublished","pages","index","all","findIndex","other","hostRef","get","result","exists","before","starter","placeholder","retry","routing","snapshot","owner","find","releases","retire","limit","layoutWrite","aiNavigationLayoutWrite","unwrittenHrefs","error","console","storeLinksWrite","layoutWrites","hostUpdates","publishedAt","deletedAt","assign","update","firstPublish","layoutIds","screen","layout","currentVersionId","draftRef","draft","fromDraft","base","next","screenPaths","hollow","packed","Buffer","from","aiJobId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-site-publish.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 { isFirstPublishedRoute } from '@aglyn/aglyn/app-utils/analytics-events'\nimport { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { hostPublicOrigin } from '@aglyn/aglyn/app-utils/host-naming'\nimport {\n blockingRouteOwner,\n normalizeScreenSlug,\n SCREEN_ROOT_PATH,\n screenRoutePathToUrl,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { resolvePageLocale, siteLanguageCode } from '@aglyn/aglyn/app-utils/seo-locale'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport { dropPluginSiteCache } from '@aglyn/aglyn/plugin-manager/plugin-site-cache'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { sendGa4SitePublished } from '@aglyn/tenant-data-admin/server/ga4-measurement-protocol'\nimport { FieldValue, type Firestore } from 'firebase-admin/firestore'\nimport {\n aiLayoutFrameHollows,\n aiLayoutFrameHollowsSaid,\n aiLayoutFrameIsHollow,\n aiLayoutRepairFrameHollows,\n aiLayoutWithoutFrameHollows,\n} from '../layout-language/ai-layout-frame-hollows'\nimport type { AiJob, AiJobOutput, AiJobSitePublish } from '../model/ai-jobs.types'\nimport { AI_SITE_BLOG_SLUGS } from '../model/ai-site-job'\nimport { aiSiteEntityUpdates, aiSiteEntityValues, aiSiteListingUpdates } from '../model/ai-site-entity'\nimport { aiSiteSeoProposalForInputs } from '../model/ai-site-start-seo'\nimport { aiLayoutWithStoreLinks, type AiStoreLayoutLinks } from '../model/ai-layout-store-links'\nimport { aiLayoutWithMenuChanges, aiNoMenuChanges } from '../model/ai-layout-menus'\nimport { AI_STORE_LINKS_SOURCE_FIELD as STORE_LINKS_SOURCE_FIELD } from './ai-job-store-links-layout'\nimport { updateAiDraftNodes } from './ai-job-drafts'\n\n/**\n * A store's links a build drafted into the site's own layout (AGL-3676): the\n * draft version, the links in it, and the site's page addresses they were\n * checked against. Its publish makes that version the live one, or — where\n * the layout changed since the draft was made — adds the same links to the\n * version that is live now.\n */\nexport interface AiSitePublishStoreLinks {\n layoutId: string\n versionId: string\n links: AiStoreLayoutLinks\n screenPaths?: Readonly<Record<string, string>>\n}\n\n/**\n * A guided site start publishes what it built (AGL-3596).\n *\n * A person who asked for \"a website\" in the guided start and watched it build\n * was then handed drafts: a site whose address still answered with the\n * starter, or with nothing. A `site` job started from the guided start (its\n * plan confirmed on the person's behalf, `inputs.autoConfirm`) therefore ends\n * by putting every page it built on the site, through the same writes a\n * person's Publish makes:\n *\n * - each page's address in the host's routing map, `publishedAt` on the page,\n * in ONE batch with the publish outbox entry that drops the live site's\n * cached pages (AGL-2575), the drop itself attempted at once;\n * - the home page takes `/` from the starter home page the site was created\n * with (AGL-3408), and an untouched starter goes to the trash rather than\n * staying beside it as a second \"Home\";\n * - the navigation entries the pages proposed are added to the header of\n * the layout they render inside, as a new live version of that layout;\n * - `site_published` once, server-side, as the scheduled publish sends it.\n *\n * A page that cannot be published — deleted meanwhile, no address, an address\n * another page holds — stays a draft and is reported with the plain sentence\n * for why. It never fails the job: the site is still built, and the person\n * fixes that one page in Pages. Every other job kind keeps producing drafts.\n *\n * The allowance is respected by construction: a draft is counted against the\n * plan's pages when it is written (`aiDraftBandRefusal`), so publishing\n * drafts adds none, and retiring the starter frees the one it held.\n */\n\nexport interface AiSitePublishDeps {\n sendSitePublished?: typeof sendGa4SitePublished\n dropCache?: typeof dropPluginSiteCache\n}\n\n/**\n * Whether a build puts its pages live (AGL-3616): only where its request\n * asked to (`publish`, set by Assist's proposal) AND the member ticked the\n * plan card's box when confirming (`publishConfirmed`, set by the resume\n * door). Every other build leaves drafts.\n */\nexport function aiJobPublishesBuild(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'build' && job.inputs?.['publish'] === true && job.inputs?.['publishConfirmed'] === true\n}\n\n/**\n * The host fields a guided start writes for its plan's language (AGL-3750),\n * as the Languages card saves one language: `locales` of that one and\n * `defaultLocale` the same. `null` — nothing written — where the plan said no\n * valid language, where it is the language the site already publishes in\n * (English for a new site), and on a site with more than one language, whose\n * languages are the owner's decision. Nothing is translated: the pages were\n * written in the brief's language, and this says so to the reader.\n */\nexport function aiSiteLanguageUpdates(\n host: { locales?: unknown; defaultLocale?: unknown },\n language: string | null | undefined,\n): { locales: string[]; defaultLocale: string } | null {\n const code = siteLanguageCode(language)\n if (!code) return null\n const locales = Array.isArray(host.locales)\n ? host.locales.filter((one): one is string => typeof one === 'string' && one.trim() !== '')\n : []\n if (locales.length > 1) return null\n const current = resolvePageLocale({\n host: {\n ...(typeof host.defaultLocale === 'string' ? { defaultLocale: host.defaultLocale } : {}),\n locales,\n },\n })\n if (current === code) return null\n return { locales: [code], defaultLocale: code }\n}\n\n/** Whether a finished job of this shape publishes what it built. */\nexport function aiJobPublishesSite(job: Pick<AiJob, 'kind' | 'inputs'>): boolean {\n return job.kind === 'site' && job.inputs?.['autoConfirm'] === true\n}\n\n/**\n * Where a site answers: its custom domain, else its subdomain under the\n * deployment's tenant apex — never a hard-coded platform host, so a\n * self-hosted deployment links to its own sites.\n */\nexport function aiSiteLiveUrl(host: { cname?: unknown; subdomain?: unknown }): string | null {\n const text = (value: unknown) => (typeof value === 'string' ? value.trim() : '')\n const origin = hostPublicOrigin({ cname: text(host.cname) || null, subdomain: text(host.subdomain) || null })\n return origin ? `${origin}/` : null\n}\n\n/** The sentences a page that stayed a draft is reported with. */\nexport const AI_SITE_PUBLISH_DELETED = 'It was deleted before it could be published.'\nexport const AI_SITE_PUBLISH_NO_ADDRESS = 'It has no address yet. Give it one in Pages and publish it.'\nexport const aiSitePublishTakenCopy = (path: string): string =>\n `Another page is already published at ${screenRoutePathToUrl(path)}. Give this one another address in Pages and publish it.`\nexport const AI_SITE_PUBLISH_FAILED = 'It could not be published just now. Publish it from Pages.'\n\n/** A navigation entry a page proposed, as the page step reports it. */\ninterface NavEntry {\n screenId: string\n label: string\n}\n\nfunction navEntriesOf(outputs: readonly AiJobOutput[]): NavEntry[] {\n const entries: NavEntry[] = []\n for (const output of outputs) {\n if (output.resource !== 'screen') continue\n const navigation = output.proposal?.['navigation'] as { label?: unknown } | undefined\n const label = typeof navigation?.label === 'string' ? navigation.label.trim() : ''\n if (label && !entries.some((entry) => entry.screenId === output.id)) {\n entries.push({ screenId: output.id, label })\n }\n }\n return entries\n}\n\ntype MutableNode = {\n $id: string\n componentId?: string\n parentId?: string\n props?: Record<string, unknown>\n nodes?: string[]\n [key: string]: unknown\n}\n\n/**\n * The blog paths a start's layout links and must not publish (AGL-3660): its\n * header, phone menu and footer link the blog by path while the posts part is\n * still owed, so a start whose posts part then failed or was skipped drops\n * every address the blog could have taken. None while the blog was written.\n */\nexport function aiSiteUnwrittenBlogHrefs(blogUnwritten: boolean | undefined): string[] {\n return blogUnwritten ? AI_SITE_BLOG_SLUGS.map((slug) => `/${slug}`) : []\n}\n\n/**\n * The layout's nodes with its links to the retired starter home pointed at\n * the new home, its links to pages the start did not write taken out, and a\n * link for each proposed entry the header does not already carry, added by\n * the navigation op's `aiLayoutWithMenuChanges` (AGL-3747): to the header's\n * menu and its phone menu, after their items, or after the brand in a\n * starter's Toolbar. `null` when nothing changes, or when the layout has no\n * header to hold an entry and no link to repoint or take out.\n */\nexport function aiLayoutWithNavigation(\n nodes: NodesMap,\n input: {\n entries: readonly NavEntry[]\n retiredHomeId?: string | null\n homeId?: string | null\n /** Paths whose links come out: a blog the start linked and never wrote (AGL-3660). */\n droppedHrefs?: readonly string[]\n /** Pages whose links come out: a page the start planned and did not put live (AGL-3988). */\n droppedScreenIds?: readonly string[]\n },\n): NodesMap | null {\n const map: Record<string, MutableNode> = {}\n for (const [id, node] of Object.entries(nodes as unknown as Record<string, MutableNode>)) {\n map[id] = { ...node, ...(node.props ? { props: { ...node.props } } : {}), ...(node.nodes ? { nodes: [...node.nodes] } : {}) }\n }\n let changed = false\n // The header, the phone menu and the footer link a blog by its path before\n // its posts are written (AGL-3660); a start whose posts were not written\n // publishes none of those links to a page that does not exist. Nor does a\n // start link a page it planned and did not put live (AGL-3988): the\n // beta.270 Sugar Hollow header linked its refused Galleries page.\n const dropped = new Set(input.droppedHrefs ?? [])\n const unbuilt = new Set(input.droppedScreenIds ?? [])\n if (dropped.size || unbuilt.size) {\n const gone = new Set(\n Object.entries(map)\n .filter(\n ([, node]) =>\n node.componentId === 'muiScreenLink' &&\n (dropped.has(String(node.props?.['href'] ?? '')) || unbuilt.has(String(node.props?.['screenId'] ?? ''))),\n )\n .map(([id]) => id),\n )\n if (gone.size) {\n for (const id of gone) delete map[id]\n for (const node of Object.values(map)) {\n if (node.nodes?.some((child) => gone.has(child))) node.nodes = node.nodes.filter((child) => !gone.has(child))\n }\n // A footer heading whose links all went goes with them, and its column\n // when that leaves it only the heading (AGL-3735).\n aiLayoutRepairFrameHollows(map as unknown as Record<string, unknown>)\n changed = true\n }\n }\n const links = Object.values(map).filter((node) => node.componentId === 'muiScreenLink')\n if (input.retiredHomeId && input.homeId) {\n for (const link of links) {\n if (link.props?.['screenId'] === input.retiredHomeId) {\n link.props = { ...link.props, screenId: input.homeId }\n changed = true\n }\n }\n }\n // The entries go into the header through the navigation op's own code\n // (AGL-3747): the bar's menu and the phone menu's, after the items already\n // there, each looking like its neighbours. A page some link already opens,\n // or an item of the same name, is not added again.\n const linked = new Set(links.map((link) => link.props?.['screenId']).filter(Boolean))\n const missing = input.entries.filter((entry) => !linked.has(entry.screenId))\n if (missing.length) {\n const added = aiLayoutWithMenuChanges(\n map,\n ['header'],\n { ...aiNoMenuChanges(), add: missing.map((entry) => ({ parent: null, label: entry.label, to: { screenId: entry.screenId } })) },\n { newId: () => `ai_nav_${createResourceUid()}` },\n )\n if (added.nodes) return added.nodes as unknown as NodesMap\n }\n return changed ? (map as unknown as NodesMap) : null\n}\n\n/** Elements that only frame what they hold, which go once a link taken out leaves them nothing. */\nconst LINK_FRAMES = new Set(['muiStack', 'muiBox', 'muiGrid', 'muiButtonGroup', 'muiCardActions'])\n\n/**\n * A page's nodes with every link to a page the start did not put live taken\n * out (AGL-3988), and every frame that held only such links with them, up to\n * its section: a published site never links to a 404. The beta.270 Sugar\n * Hollow home page linked its refused Galleries page from \"See the\n * galleries\" and the dataset band's \"View all galleries\". A link is a Page\n * Link to one of `screenIds`; anything else that opens one (a picture) keeps\n * its place and loses the link. `null` when nothing links one. Pure.\n */\nexport function aiPageWithoutLinksTo(nodes: NodesMap, screenIds: readonly string[]): NodesMap | null {\n const missing = new Set(screenIds.filter(Boolean))\n if (!missing.size) return null\n const map: Record<string, MutableNode> = {}\n for (const [id, node] of Object.entries(nodes as unknown as Record<string, MutableNode>)) {\n map[id] = { ...node, ...(node.props ? { props: { ...node.props } } : {}), ...(node.nodes ? { nodes: [...node.nodes] } : {}) }\n }\n const opensMissing = (node: MutableNode | undefined) => missing.has(String(node?.props?.['screenId'] ?? ''))\n const ids = Object.keys(map).filter((id) => opensMissing(map[id]))\n if (!ids.length) return null\n const parentOf = (id: string) => Object.keys(map).find((key) => map[key]?.nodes?.includes(id)) ?? null\n const removeTree = (id: string) => {\n for (const child of [...(map[id]?.nodes ?? [])]) removeTree(child)\n delete map[id]\n }\n for (const id of ids) {\n const node = map[id]\n if (!node) continue\n if (node.componentId !== 'muiScreenLink') {\n const props = { ...(node.props ?? {}) }\n delete props['screenId']\n node.props = props\n continue\n }\n let parent = parentOf(id)\n if (parent) map[parent] = { ...(map[parent] as MutableNode), nodes: (map[parent]?.nodes ?? []).filter((child) => child !== id) }\n removeTree(id)\n // The frames it leaves empty, never a section nor the page's root.\n while (parent && map[parent] && !(map[parent]?.nodes ?? []).length && LINK_FRAMES.has(String(map[parent]?.componentId))) {\n const above = parentOf(parent)\n if (!above) break\n map[above] = { ...(map[above] as MutableNode), nodes: (map[above]?.nodes ?? []).filter((child) => child !== parent) }\n delete map[parent]\n parent = above\n }\n }\n return map as unknown as NodesMap\n}\n\n/** The blog a start writes, as its header entry links it (`aiSiteBlogNavPage`). */\nexport interface AiSiteBlogEntry {\n label: string\n href: string\n}\n\n/**\n * The layout's nodes with the blog's header entry put back (AGL-3747): a\n * start whose posts part failed published with every link to the blog taken\n * out (AGL-3660), and Try again's posts went live with none to reach them.\n * Added by the navigation op's `aiLayoutWithMenuChanges`, as the start's\n * other entries are — the bar's menu and the phone menu — second after the\n * home page's item, as the start first linked it (AGL-3676), else last.\n * `null` where some link already opens the blog, or the layout has no header\n * menu to hold it.\n */\nexport function aiLayoutWithBlogEntry(\n nodes: NodesMap,\n input: { blog: AiSiteBlogEntry; homeId?: string | null },\n): NodesMap | null {\n const blogHrefs = new Set([input.blog.href, ...AI_SITE_BLOG_SLUGS.map((slug) => `/${slug}`)])\n const linked = Object.values(nodes as unknown as Record<string, MutableNode>).some(\n (node) => node?.componentId === 'muiScreenLink' && blogHrefs.has(String(node.props?.['href'] ?? '')),\n )\n if (linked) return null\n const home = [...(input.homeId ? [{ screenId: input.homeId }] : []), { href: '/' }]\n const added = aiLayoutWithMenuChanges(\n nodes as unknown as Record<string, unknown>,\n ['header'],\n { ...aiNoMenuChanges(), add: [{ parent: null, label: input.blog.label, to: { href: input.blog.href }, after: home }] },\n { newId: () => `ai_nav_${createResourceUid()}` },\n )\n return (added.nodes as unknown as NodesMap | null) ?? null\n}\n\n/**\n * Puts the blog's header entry back on a live start whose posts Try again\n * wrote (AGL-3747), as a new live version of the layout its published pages\n * render inside, in one batch with the publish outbox entry that drops the\n * live site's cached pages, the drop itself attempted at once. Returns\n * whether the entry went live; never throws for a layout it cannot change.\n */\nexport async function aiRestoreGuidedBlogEntry(\n firestore: Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'hostId'>\n /** The start's published pages: the layout they share is the one changed. */\n pageIds: readonly string[]\n /** The published home page, which the blog's entry follows. */\n homeId?: string | null\n blog: AiSiteBlogEntry\n now: Date\n },\n deps: Pick<AiSitePublishDeps, 'dropCache'> = {},\n): Promise<boolean> {\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const hostId = input.job.hostId ?? ''\n if (!hostId || !input.pageIds.length) return false\n const layoutId = await aiPagesLayoutId(firestore, hostId, input.pageIds)\n if (!layoutId) return false\n const write = await aiLayoutVersionWrite(firestore, {\n hostId,\n layoutId,\n transform: (nodes) => aiLayoutWithBlogEntry(nodes, { blog: input.blog, homeId: input.homeId ?? null }),\n storeLinks: null,\n jobId: input.job.$id,\n now: input.now,\n displayName: 'Navigation for the blog',\n })\n if (!write?.version) return false\n const batch = firestore.batch()\n batch.create(write.versionRef, write.version)\n batch.set(write.layoutRef, { versionId: write.versionRef.id, updatedAt: input.now }, { merge: true })\n // A layout change reaches every page, so the announce asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n entireHost: true,\n })\n await batch.commit()\n const dropped = await dropCache({ hostIds: [hostId], reason: 'guided AI site start linked its blog' })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n return true\n}\n\n/**\n * A retry's publish merged into the start's first (AGL-3730): the pages it\n * put live join the published list, a page it published leaves the drafts,\n * and a page it left a draft is reported with its newest reason.\n */\nexport function aiMergeSitePublish(previous: AiJobSitePublish, retried: AiJobSitePublish): AiJobSitePublish {\n const now = new Set([...retried.published, ...retried.drafts].map((page) => page.id))\n return {\n liveUrl: retried.liveUrl ?? previous.liveUrl,\n published: [...previous.published.filter((page) => !now.has(page.id)), ...retried.published],\n drafts: [...previous.drafts.filter((page) => !now.has(page.id)), ...retried.drafts],\n }\n}\n\n/**\n * Publishes the pages a guided site start built. Never throws for a page; a\n * Firestore failure of the publish write itself reports every page as a\n * draft, for the same reason, and is logged.\n */\nexport async function aiPublishGuidedSite(\n firestore: Firestore,\n input: {\n job: Pick<AiJob, '$id' | 'orgId' | 'hostId'> & Partial<Pick<AiJob, 'inputs'>>\n outputs: readonly AiJobOutput[]\n now: Date\n /** The start owed its blog's posts and wrote none (AGL-3660): its links to the blog come out. */\n blogUnwritten?: boolean\n /** Paths the layout links that the start did not write, such as a store page (AGL-3676): their links come out. */\n unwrittenHrefs?: readonly string[]\n /**\n * The pages the start planned and did not build (AGL-3988): a page its\n * checks refused. Every link to one — the header's, the footer's, a\n * button's on a page going live — comes out, and so does every link to\n * a page this publish leaves a draft.\n */\n unbuiltScreenIds?: readonly string[]\n /**\n * The start's pages in the order the site lists them (AGL-3988): a page\n * going live with no place of its own in Pages takes its place there, so\n * the 404 page's links follow the site's order, never the alphabet.\n */\n siteOrder?: readonly string[]\n /** A store's links a build drafted into the site's own layout (AGL-3676), which go live with its pages. */\n storeLinks?: AiSitePublishStoreLinks | null\n /**\n * The start published once already, and Try again rebuilt what failed\n * (AGL-3730): only `outputs` go live, beside what is. The home address is\n * never taken from the starter here, the starter is never retired, and\n * `site_published` is not sent again; a retried page at `/` that the\n * starter still holds stays a draft and says so.\n */\n retry?: boolean\n /**\n * The language the plan says the brief and copy are in (AGL-3750): the\n * site publishes in it, where `aiSiteLanguageUpdates` says so.\n */\n language?: string | null\n },\n deps: AiSitePublishDeps = {},\n): Promise<AiJobSitePublish> {\n const sendSitePublished = deps.sendSitePublished ?? sendGa4SitePublished\n const dropCache = deps.dropCache ?? dropPluginSiteCache\n const { job, now } = input\n const hostId = job.hostId ?? ''\n const pages = input.outputs.filter(\n (output, index, all) =>\n output.resource === 'screen' &&\n (output.hostId ?? hostId) === hostId &&\n all.findIndex((other) => other.resource === 'screen' && other.id === output.id) === index,\n )\n const hostRef = firestore.collection('hosts').doc(hostId)\n const host = await hostRef.get()\n const result: AiJobSitePublish = {\n liveUrl: aiSiteLiveUrl({ cname: host.get('cname'), subdomain: host.get('subdomain') }),\n published: [],\n drafts: [],\n }\n if (!host.exists || !pages.length) return result\n\n const before = { ...((host.get('screens') ?? {}) as Record<string, string>) }\n const starter = (host.get('defaultHomeScreenId') as string | undefined) || null\n // A retry never takes `/` from the starter (AGL-3730): it holds it like any page.\n const placeholder = input.retry ? null : starter\n const routing = { ...before }\n const entries: Record<string, string> = {}\n const accepted: Array<{ id: string; label: string; path: string; slug: string; order?: number }> = []\n const siteOrder = input.siteOrder ?? []\n for (const page of pages) {\n const snapshot = await hostRef.collection('screens').doc(page.id).get()\n const label = String(snapshot.get('displayName') ?? page.label ?? '') || page.label\n if (!snapshot.exists || snapshot.get('deletedAt') != null) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_DELETED })\n continue\n }\n const slug = normalizeScreenSlug(String(snapshot.get('slug') ?? ''))\n if (!slug) {\n result.drafts.push({ id: page.id, label, reason: AI_SITE_PUBLISH_NO_ADDRESS })\n continue\n }\n const owner = blockingRouteOwner(routing, slug, placeholder)\n if (owner && owner !== page.id) {\n result.drafts.push({ id: page.id, label, reason: aiSitePublishTakenCopy(slug) })\n continue\n }\n routing[page.id] = slug\n entries[page.id] = slug\n // Its place in Pages, where the owner has not given it one (AGL-3988).\n const place = siteOrder.indexOf(page.id)\n const placed = typeof snapshot.get('order') === 'number'\n accepted.push({ id: page.id, label, path: slug, slug, ...(place >= 0 && !placed ? { order: place } : {}) })\n }\n if (!accepted.length) return result\n\n // A page that is not live once this publish is done — planned and never\n // built, or left a draft just now — is linked from nowhere (AGL-3988).\n const live = new Set([...accepted.map((page) => page.id), ...Object.keys(before)])\n const notLive = [...new Set([...(input.unbuiltScreenIds ?? []), ...result.drafts.map((page) => page.id)])].filter(\n (id) => id && !live.has(id),\n )\n if (notLive.length) {\n for (const page of accepted) {\n await updateAiDraftNodes(firestore, {\n kind: 'screen',\n hostId,\n id: page.id,\n now,\n update: (nodes) => aiPageWithoutLinksTo(nodes, notLive),\n }).catch((error: unknown) => {\n console.error('ai site publish: links to an unbuilt page not taken out', { hostId, jobId: job.$id, pageId: page.id, error })\n })\n }\n }\n\n // The first real home page replaces the placeholder in the same write.\n const home = accepted.find((page) => page.path === SCREEN_ROOT_PATH) ?? null\n const releases =\n home && placeholder && placeholder !== home.id && before[placeholder] === SCREEN_ROOT_PATH\n ? placeholder\n : null\n // An untouched starter home (still its one first version) is retired to the\n // trash; one its owner edited stays in Pages as their draft.\n const retire = releases\n ? (await hostRef.collection('screens').doc(releases).collection('versions').limit(2).get()).size <= 1\n : false\n\n // The header of the layout the pages render inside learns their entries.\n const layoutWrite = await aiNavigationLayoutWrite(firestore, {\n hostId,\n pageIds: accepted.map((page) => page.id),\n entries: navEntriesOf(pages).filter((entry) => entries[entry.screenId]),\n droppedHrefs: [...aiSiteUnwrittenBlogHrefs(input.blogUnwritten), ...(input.unwrittenHrefs ?? [])],\n droppedScreenIds: notLive,\n retiredHomeId: releases,\n homeId: home?.id ?? null,\n jobId: job.$id,\n now,\n storeLinks: input.storeLinks ?? null,\n }).catch((error: unknown) => {\n console.error('ai site publish: navigation not applied', { hostId, jobId: job.$id, error })\n return null\n })\n // A store's links drafted into a layout the pages' navigation did not touch go live on their own.\n const storeLinks = input.storeLinks ?? null\n const storeLinksWrite =\n storeLinks && layoutWrite?.layoutRef.id !== storeLinks.layoutId\n ? await aiLayoutVersionWrite(firestore, {\n hostId,\n layoutId: storeLinks.layoutId,\n transform: () => null,\n storeLinks,\n jobId: job.$id,\n now,\n }).catch((error: unknown) => {\n console.error('ai site publish: store links not applied', { hostId, jobId: job.$id, error })\n return null\n })\n : null\n const layoutWrites = [layoutWrite, storeLinksWrite].filter((write): write is LayoutWrite => Boolean(write))\n\n const paths = [\n ...new Set(\n [\n ...Object.values(entries),\n ...(releases ? [SCREEN_ROOT_PATH] : []),\n ].map((path) => screenRoutePathToUrl(path)),\n ),\n ]\n const batch = firestore.batch()\n const hostUpdates: Record<string, unknown> = { updatedAt: now }\n for (const [id, path] of Object.entries(entries)) hostUpdates[`screens.${id}`] = path\n if (releases) {\n hostUpdates[`screens.${releases}`] = FieldValue.delete()\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n batch.set(\n hostRef.collection('screens').doc(releases),\n { publishedAt: FieldValue.delete(), ...(retire ? { deletedAt: now } : {}) },\n { merge: true },\n )\n } else if (placeholder && entries[placeholder]) {\n hostUpdates['defaultHomeScreenId'] = FieldValue.delete()\n }\n // The site's language, from its plan (AGL-3750): every page's `<html lang>`\n // reads it, so the announce below asks for the whole site.\n const language = aiSiteLanguageUpdates(\n { locales: host.get('locales'), defaultLocale: host.get('defaultLocale') },\n input.language,\n )\n if (language) Object.assign(hostUpdates, language)\n // Who publishes the site, from the start's answers (AGL-3979): its Setup ->\n // SEO -> Entity and AI agents fields and its Business details, each only\n // where empty, so every page's structured data and the site's /llms.txt\n // name its business type, phone, email, address, hours, areas, profiles and\n // logo. Every page carries the entity, so the announce asks for the whole\n // site when any of it changes.\n const siteName = typeof host.get('displayName') === 'string' ? (host.get('displayName') as string) : null\n const entity = aiSiteEntityUpdates(\n host.get('seo'),\n aiSiteEntityValues(job.inputs, { language: input.language ?? null, siteName }),\n { logoUrl: host.get('logoUrl'), business: host.get('business') },\n )\n Object.assign(hostUpdates, entity)\n // The site's own listing (AGL-3979): the start's, with the site's name,\n // over the title and description its creation seeded and nowhere else. A\n // listing the owner changed keeps their words, and the SEO section still\n // offers the start's beside them. Every page falls back to it, and\n // `og:site_name` and /llms.txt read it, so it asks for the whole site too.\n const listing = aiSiteListingUpdates(\n host.get('seo'),\n siteName,\n aiSiteSeoProposalForInputs(job.inputs, input.language ?? null, siteName),\n )\n Object.assign(hostUpdates, listing)\n const entireHost =\n layoutWrites.length > 0 || language !== null || Object.keys(entity).length > 0 || Object.keys(listing).length > 0\n batch.update(hostRef, hostUpdates)\n for (const page of accepted) {\n batch.set(\n hostRef.collection('screens').doc(page.id),\n { slug: page.slug, publishedAt: now, ...(page.order !== undefined ? { order: page.order } : {}) },\n { merge: true },\n )\n }\n for (const write of layoutWrites) {\n if (write.version) batch.create(write.versionRef, write.version)\n batch.set(write.layoutRef, { versionId: write.versionRef.id, updatedAt: now }, { merge: true })\n }\n // The announce, written down in the publish's own batch (AGL-2575): a\n // layout change reaches every page, so it asks for the whole site.\n const outboxRef = firestore.collection('publishOutbox').doc(createResourceUid())\n batch.set(outboxRef, {\n hostId,\n paths: paths.length ? paths : [SCREEN_ROOT_PATH],\n createdAt: FieldValue.serverTimestamp(),\n attempts: 0,\n ...(entireHost ? { entireHost: true } : {}),\n })\n try {\n await batch.commit()\n } catch (error) {\n console.error('ai site publish failed', { hostId, jobId: job.$id, error })\n for (const page of accepted) result.drafts.push({ id: page.id, label: page.label, reason: AI_SITE_PUBLISH_FAILED })\n return result\n }\n result.published = accepted.map(({ id, label, path }) => ({ id, label, path: screenRoutePathToUrl(path) }))\n\n // The live site's cached pages go now; the outbox entry stays for the\n // drain unless the drop certainly happened.\n const dropped = await dropCache({\n hostIds: [hostId],\n reason: 'guided AI site start published',\n ...(entireHost ? {} : { paths: { [hostId]: paths } }),\n })\n if (dropped.complete) await outboxRef.delete().catch(() => undefined)\n // A site that came alive with nobody's browser on the publish (AGL-1589),\n // announced once: a retry's pages join a site already announced (AGL-3730).\n if (!input.retry) await sendSitePublished({ hostId, firstPublish: isFirstPublishedRoute(before, placeholder) })\n return result\n}\n\ninterface LayoutWrite {\n layoutRef: FirebaseFirestore.DocumentReference\n versionRef: FirebaseFirestore.DocumentReference\n /** The new version to create; `null` where the live version becomes one already written (a store's links draft). */\n version: Record<string, unknown> | null\n}\n\n/**\n * The new live version of the layout the published pages render inside, with\n * their navigation entries and the home link moved off a retired starter;\n * `null` when there is nothing to change or no one layout to change.\n */\nasync function aiNavigationLayoutWrite(\n firestore: Firestore,\n input: {\n hostId: string\n pageIds: readonly string[]\n entries: readonly NavEntry[]\n droppedHrefs: readonly string[]\n droppedScreenIds: readonly string[]\n retiredHomeId: string | null\n homeId: string | null\n jobId: string\n now: Date\n storeLinks?: AiSitePublishStoreLinks | null\n },\n): Promise<LayoutWrite | null> {\n if (!input.entries.length && !input.retiredHomeId && !input.droppedHrefs.length && !input.droppedScreenIds.length && !input.storeLinks) return null\n const layoutId = await aiPagesLayoutId(firestore, input.hostId, input.pageIds)\n if (!layoutId) return null\n return aiLayoutVersionWrite(firestore, {\n hostId: input.hostId,\n layoutId,\n transform: (nodes) => aiLayoutWithNavigation(nodes, input),\n storeLinks: input.storeLinks ?? null,\n jobId: input.jobId,\n now: input.now,\n })\n}\n\n/** The one layout the pages render inside, by their current versions; `null` where they do not share one. */\nasync function aiPagesLayoutId(firestore: Firestore, hostId: string, pageIds: readonly string[]): Promise<string | null> {\n const hostRef = firestore.collection('hosts').doc(hostId)\n const layoutIds = new Set<string>()\n for (const id of pageIds) {\n const screen = await hostRef.collection('screens').doc(id).get()\n const versionId = screen.get('versionId')\n const version =\n typeof versionId === 'string' && versionId\n ? await hostRef.collection('screens').doc(id).collection('versions').doc(versionId).get()\n : null\n const layoutId = version?.get('layoutId') ?? screen.get('layoutId')\n if (typeof layoutId === 'string' && layoutId) layoutIds.add(layoutId)\n }\n return layoutIds.size === 1 ? [...layoutIds][0] : null\n}\n\n/**\n * The new live version of one layout: `transform` applied to the version\n * live now. A store's links drafted into this layout (AGL-3676) start it from\n * that draft while the draft was made from the live version, and are added\n * to the live version otherwise; a draft with nothing more to add becomes\n * the live version as it is. `null` when nothing changes.\n */\nasync function aiLayoutVersionWrite(\n firestore: Firestore,\n input: {\n hostId: string\n layoutId: string\n transform: (nodes: NodesMap) => NodesMap | null\n storeLinks: AiSitePublishStoreLinks | null\n jobId: string\n now: Date\n /** The new version's name in the layout's versions. */\n displayName?: string\n },\n): Promise<LayoutWrite | null> {\n const { layoutId } = input\n const layoutRef = firestore.collection('hosts').doc(input.hostId).collection('layouts').doc(layoutId)\n const layout = await layoutRef.get()\n const currentVersionId = layout.get('versionId')\n if (!layout.exists || layout.get('deletedAt') != null || typeof currentVersionId !== 'string') return null\n const current = await layoutRef.collection('versions').doc(currentVersionId).get()\n const links = input.storeLinks?.layoutId === layoutId ? input.storeLinks : null\n const draftRef = links ? layoutRef.collection('versions').doc(links.versionId) : null\n const draft = draftRef ? await draftRef.get() : null\n const fromDraft = Boolean(draft?.exists && draft.get(STORE_LINKS_SOURCE_FIELD) === currentVersionId)\n const base = fromDraft && draft ? draft : current\n const nodes = base.exists ? decodeStoredNodes<NodesMap>(base.get('nodes')) : null\n if (!nodes) return null\n let next = input.transform(nodes)\n if (links && !fromDraft) {\n const added = aiLayoutWithStoreLinks((next ?? nodes) as never, links.links, links.screenPaths)\n next = (added.nodes as NodesMap | null) ?? next\n }\n // What a publish puts live has no hollow heading, column or menu (AGL-3735).\n next = (aiLayoutWithoutFrameHollows((next ?? nodes) as unknown as Record<string, unknown>) as NodesMap | null) ?? next\n const hollow = aiLayoutFrameHollows((next ?? nodes) as unknown as Record<string, unknown>)\n if (aiLayoutFrameIsHollow(hollow)) {\n console.error('ai site publish: a layout is still hollow after its repair', { layoutId, hollow: aiLayoutFrameHollowsSaid(hollow) })\n }\n if (!next) return fromDraft && draftRef ? { layoutRef, versionRef: draftRef, version: null } : null\n const packed = encodeStoredNodes(next)\n if (!packed) return null\n const versionRef = layoutRef.collection('versions').doc(createResourceUid())\n return {\n layoutRef,\n versionRef,\n version: {\n layoutId,\n hostId: input.hostId,\n displayName: input.displayName ?? 'Navigation for the new pages',\n nodes: Buffer.from(packed),\n createdAt: input.now,\n updatedAt: input.now,\n aiJobId: input.jobId,\n },\n }\n}\n"],"names":["isFirstPublishedRoute","createResourceUid","hostPublicOrigin","blockingRouteOwner","normalizeScreenSlug","SCREEN_ROOT_PATH","screenRoutePathToUrl","resolvePageLocale","siteLanguageCode","decodeStoredNodes","encodeStoredNodes","dropPluginSiteCache","sendGa4SitePublished","FieldValue","aiLayoutFrameHollows","aiLayoutFrameHollowsSaid","aiLayoutFrameIsHollow","aiLayoutRepairFrameHollows","aiLayoutWithoutFrameHollows","AI_SITE_BLOG_SLUGS","aiSiteEntityUpdates","aiSiteEntityValues","aiSiteListingUpdates","aiSiteSeoProposalForInputs","aiLayoutWithStoreLinks","aiLayoutWithMenuChanges","aiNoMenuChanges","AI_STORE_LINKS_SOURCE_FIELD","STORE_LINKS_SOURCE_FIELD","updateAiDraftNodes","aiJobPublishesBuild","job","kind","inputs","aiSiteLanguageUpdates","host","language","code","locales","Array","isArray","filter","one","trim","length","current","defaultLocale","aiJobPublishesSite","aiSiteLiveUrl","text","value","origin","cname","subdomain","AI_SITE_PUBLISH_DELETED","AI_SITE_PUBLISH_NO_ADDRESS","aiSitePublishTakenCopy","path","AI_SITE_PUBLISH_FAILED","navEntriesOf","outputs","entries","output","resource","navigation","proposal","label","some","entry","screenId","id","push","aiSiteUnwrittenBlogHrefs","blogUnwritten","map","slug","aiLayoutWithNavigation","nodes","input","node","Object","props","changed","dropped","Set","droppedHrefs","unbuilt","droppedScreenIds","size","gone","componentId","has","String","values","child","links","retiredHomeId","homeId","link","linked","Boolean","missing","added","add","parent","to","newId","LINK_FRAMES","aiPageWithoutLinksTo","screenIds","opensMissing","ids","keys","parentOf","find","key","includes","removeTree","above","aiLayoutWithBlogEntry","blogHrefs","blog","href","home","after","aiRestoreGuidedBlogEntry","firestore","deps","dropCache","hostId","pageIds","layoutId","aiPagesLayoutId","write","aiLayoutVersionWrite","transform","storeLinks","jobId","$id","now","displayName","version","batch","create","versionRef","set","layoutRef","versionId","updatedAt","merge","outboxRef","collection","doc","paths","createdAt","serverTimestamp","attempts","entireHost","commit","hostIds","reason","complete","delete","catch","undefined","aiMergeSitePublish","previous","retried","published","drafts","page","liveUrl","aiPublishGuidedSite","accepted","sendSitePublished","pages","index","all","findIndex","other","hostRef","get","result","exists","before","starter","placeholder","retry","routing","siteOrder","snapshot","owner","place","indexOf","placed","order","live","notLive","unbuiltScreenIds","update","error","console","pageId","releases","retire","limit","layoutWrite","aiNavigationLayoutWrite","unwrittenHrefs","storeLinksWrite","layoutWrites","hostUpdates","publishedAt","deletedAt","assign","siteName","entity","logoUrl","business","listing","firstPublish","layoutIds","screen","layout","currentVersionId","draftRef","draft","fromDraft","base","next","screenPaths","hollow","packed","Buffer","from","aiJobId"],"mappings":"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"}
|
|
@@ -109,6 +109,22 @@ export declare function fetchPublicText(url: string, options: {
|
|
|
109
109
|
text: string;
|
|
110
110
|
url: string;
|
|
111
111
|
} | null>;
|
|
112
|
+
/**
|
|
113
|
+
* A public page as it was read: its markup and the text of its first
|
|
114
|
+
* stylesheets ({@link AI_THEME_REFERENCE_STYLESHEETS}), each under its byte
|
|
115
|
+
* cap. What a theme job takes its colors from and a site start its
|
|
116
|
+
* inspiration signals (AGL-3975); never a crawl, one page and its sheets.
|
|
117
|
+
*/
|
|
118
|
+
export interface AiReferencePage {
|
|
119
|
+
/** The address the markup was finally read at, after redirects. */
|
|
120
|
+
url: string;
|
|
121
|
+
html: string;
|
|
122
|
+
stylesheets: string[];
|
|
123
|
+
}
|
|
124
|
+
/** A page and its first stylesheets through {@link fetchPublicText}; `null` when the page cannot be read. */
|
|
125
|
+
export declare function readReferencePage(url: URL, signal: AbortSignal): Promise<AiReferencePage | null>;
|
|
126
|
+
/** A read page's brand colors: every color its markup and stylesheets use, ranked. */
|
|
127
|
+
export declare function referencePageColors(page: Pick<AiReferencePage, 'html' | 'stylesheets'>): string[];
|
|
112
128
|
/** The two reads that leave the process, replaceable where a spec needs to. */
|
|
113
129
|
export interface AiThemeBrandSeams {
|
|
114
130
|
logoColors?: (firestore: FirebaseFirestore.Firestore, location: AiThemeLogoLocation, hostId: string) => Promise<string[] | null>;
|
|
@@ -312,28 +312,42 @@ async function readCapped(body, maxBytes) {
|
|
|
312
312
|
}
|
|
313
313
|
return null;
|
|
314
314
|
}
|
|
315
|
-
/** A
|
|
315
|
+
/** A page and its first stylesheets through {@link fetchPublicText}; `null` when the page cannot be read. */ export async function readReferencePage(url, signal) {
|
|
316
316
|
const page = await fetchPublicText(url.toString(), {
|
|
317
317
|
maxBytes: AI_THEME_REFERENCE_MAX_BYTES,
|
|
318
318
|
accept: /text\/html|application\/xhtml\+xml/i,
|
|
319
319
|
signal
|
|
320
320
|
});
|
|
321
321
|
if (!page) return null;
|
|
322
|
-
const
|
|
322
|
+
const stylesheets = [];
|
|
323
323
|
for (const sheet of stylesheetUrlsOf(page.text, page.url)){
|
|
324
324
|
const css = await fetchPublicText(sheet, {
|
|
325
325
|
maxBytes: AI_THEME_STYLESHEET_MAX_BYTES,
|
|
326
326
|
accept: /text\/css/i,
|
|
327
327
|
signal
|
|
328
328
|
});
|
|
329
|
-
if (
|
|
330
|
-
|
|
329
|
+
if (css) stylesheets.push(css.text);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
url: page.url,
|
|
333
|
+
html: page.text,
|
|
334
|
+
stylesheets
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** A read page's brand colors: every color its markup and stylesheets use, ranked. */ export function referencePageColors(page) {
|
|
338
|
+
const weights = colorsFromMarkup(page.html);
|
|
339
|
+
for (const css of page.stylesheets){
|
|
340
|
+
for (const [hex, weight] of colorsFromMarkup(css)){
|
|
331
341
|
var _weights_get;
|
|
332
342
|
weights.set(hex, ((_weights_get = weights.get(hex)) != null ? _weights_get : 0) + weight);
|
|
333
343
|
}
|
|
334
344
|
}
|
|
335
345
|
return rankBrandColors(weights);
|
|
336
346
|
}
|
|
347
|
+
/** A linked page's colors, from its markup and its first stylesheets. */ async function readReferenceColors(url, signal) {
|
|
348
|
+
const page = await readReferencePage(url, signal);
|
|
349
|
+
return page ? referencePageColors(page) : null;
|
|
350
|
+
}
|
|
337
351
|
export async function gatherAiThemeBrandInputs(input, seams = {}) {
|
|
338
352
|
const colors = [
|
|
339
353
|
...organizationBrandColors(input.org)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-theme-brand-inputs.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type {\n AglynOrgBilling,\n OrgBrandingProfile,\n} from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { normalizeThemeHex } from '../tools/ai-theme-tool'\nimport { AI_THEME_BRAND_BUDGET_MS } from './ai-job-theme-budget'\nimport {\n aiMediaAssetLocation,\n loadAiMediaSharp,\n readAiMediaAssetBytes,\n type AiMediaAssetLocation,\n} from './ai-media-asset'\n\n/**\n * The brand a theme job builds on (AGL-2938): the colors that belong to the\n * site before anyone describes them.\n *\n * - **The workspace's brand color** — a white-label workspace's, and only\n * theirs: without the entitlement the resolved profile is the platform's,\n * which is no customer's brand.\n * - **The site logo** — when the logo is an asset of the site's own media\n * library or its org's, read from storage and reduced to its dominant\n * colors. Never fetched from a URL: a logo that is not a library asset is\n * left alone.\n * - **A page the brief links to** — fetched through the same SSRF guard the\n * plugin fetch proxy uses (a public address, pinned; https only; every\n * redirect re-validated), capped in bytes and time, and reduced to the\n * colors its markup and stylesheets use.\n *\n * Only hex colors leave this module. Nothing a page or a logo SAYS reaches a\n * prompt, so there is nothing in them to follow. Each source is best effort:\n * one that cannot be read is named in a note and the proposal is made\n * without it.\n */\n\nexport type AiThemeBrandSource = 'organization' | 'logo' | 'reference'\n\n/** How the prompt names where each brand color came from. */\nexport const AI_THEME_BRAND_SOURCE_WORDS: Record<AiThemeBrandSource, string> = {\n organization: 'the workspace brand color',\n logo: 'from the site logo',\n reference: 'from the page the brief links to',\n}\n\nexport interface AiThemeBrandColor {\n hex: string\n source: AiThemeBrandSource\n}\n\nexport interface AiThemeBrandInputs {\n colors: AiThemeBrandColor[]\n /** A source that could not be read, in customer-safe words. */\n notes: string[]\n}\n\n/** The most colors one source contributes. */\nexport const AI_THEME_BRAND_COLORS_PER_SOURCE = 4\n\n/** The largest logo decoded for its colors. */\nexport const AI_THEME_LOGO_MAX_BYTES = 5 * 1024 * 1024\n\n/** The most of a linked page, and of each of its stylesheets, that is read. */\nexport const AI_THEME_REFERENCE_MAX_BYTES = 512 * 1024\nexport const AI_THEME_STYLESHEET_MAX_BYTES = 256 * 1024\n\n/** The stylesheets of a linked page that are read beside it. */\nexport const AI_THEME_REFERENCE_STYLESHEETS = 2\n\n/**\n * The wall clock brand colors may take, declared with the theme step's time\n * (`ai-job-theme-budget.ts`), which counts it (AGL-3035).\n */\nexport { AI_THEME_BRAND_BUDGET_MS }\n\nconst FETCH_TIMEOUT_MS = 4_000\nconst MAX_REDIRECT_HOPS = 3\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308])\n\ntype Rgb = [red: number, green: number, blue: number]\n\nconst toHex = ([red, green, blue]: Rgb) =>\n `#${[red, green, blue]\n .map((channel) => Math.round(Math.min(255, Math.max(0, channel))).toString(16).padStart(2, '0'))\n .join('')}`\n\nconst channelsOf = (hex: string): Rgb => [\n parseInt(hex.slice(1, 3), 16),\n parseInt(hex.slice(3, 5), 16),\n parseInt(hex.slice(5, 7), 16),\n]\n\nconst distance = (a: Rgb, b: Rgb) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\n\n/** How far a color is from grey: the spread of its channels. */\nconst chroma = ([red, green, blue]: Rgb) => Math.max(red, green, blue) - Math.min(red, green, blue)\n\n/** Colors closer than this read as the same color in a palette. */\nconst SAME_COLOR_DISTANCE = 40\n\n/** A spread under this reads as a grey, which says little about a brand. */\nconst GREY_CHROMA = 38\n\n/** The distinct colors of a ranked list, colorful ones first when there are any. */\nfunction distinctColors(ranked: readonly Rgb[], count: number): string[] {\n const colorful = ranked.filter((color) => chroma(color) > GREY_CHROMA)\n const chosen: Rgb[] = []\n for (const color of colorful.length ? colorful : ranked) {\n if (chosen.some((other) => distance(other, color) < SAME_COLOR_DISTANCE)) continue\n chosen.push(color)\n if (chosen.length === count) break\n }\n return chosen.map(toHex)\n}\n\n/* ------------------------------------------------------------------------ *\n * The workspace's brand color\n * ------------------------------------------------------------------------ */\n\n/** A white-label workspace's own brand color; nothing for any other workspace. */\nexport function organizationBrandColors(\n org: Partial<AglynOrgBilling> | null | undefined,\n): AiThemeBrandColor[] {\n if (!checkEntitlement(org, 'whiteLabel')) return []\n const raw = (org?.brandingProfile as OrgBrandingProfile | undefined)?.primaryColor\n const hex = typeof raw === 'string' ? normalizeThemeHex(raw) : null\n return hex ? [{ hex, source: 'organization' }] : []\n}\n\n/* ------------------------------------------------------------------------ *\n * The site logo\n * ------------------------------------------------------------------------ */\n\nexport type AiThemeLogoLocation = AiMediaAssetLocation\n\n/**\n * Where a site's logo lives in a media library: a media reference or a CDN\n * path naming this site's library, or its org's. Anything else — an external\n * URL, an asset of some other site or org — has no location, and the logo is\n * left alone. The media library's own rule, shared with every job that reads\n * an asset (`ai-media-asset.ts`).\n */\nexport const logoMediaLocation = aiMediaAssetLocation\n\n/**\n * The dominant colors of an image's pixels: transparent pixels skipped, the\n * rest grouped into coarse buckets, the busiest distinct buckets first, and\n * the near-white and near-black edges a logo sits on dropped while anything\n * else is there.\n */\nexport function dominantColorsFromPixels(\n data: ArrayLike<number>,\n channels: number,\n count = AI_THEME_BRAND_COLORS_PER_SOURCE,\n): string[] {\n const buckets = new Map<number, { sum: Rgb; pixels: number }>()\n for (let index = 0; index + channels <= data.length; index += channels) {\n if (channels >= 4 && data[index + 3] < 128) continue\n const pixel: Rgb = [data[index], data[index + 1], data[index + 2]]\n const key = ((pixel[0] >> 4) << 8) | ((pixel[1] >> 4) << 4) | (pixel[2] >> 4)\n const bucket = buckets.get(key) ?? { sum: [0, 0, 0] as Rgb, pixels: 0 }\n bucket.sum = [bucket.sum[0] + pixel[0], bucket.sum[1] + pixel[1], bucket.sum[2] + pixel[2]]\n bucket.pixels += 1\n buckets.set(key, bucket)\n }\n const ranked = [...buckets.values()]\n .sort((a, b) => b.pixels - a.pixels)\n .map(({ sum, pixels }): Rgb => [sum[0] / pixels, sum[1] / pixels, sum[2] / pixels])\n const edge = (color: Rgb) => Math.min(...color) > 235 || Math.max(...color) < 20\n const inside = ranked.filter((color) => !edge(color))\n return distinctColors(inside.length ? inside : ranked, count)\n}\n\n/** The part of `sharp` the logo read uses. */\ninterface SharpPixelPipeline {\n resize(width: number, height: number, options: { fit: 'inside' }): SharpPixelPipeline\n ensureAlpha(): SharpPixelPipeline\n raw(): SharpPixelPipeline\n toBuffer(options: { resolveWithObject: true }): Promise<{\n data: Buffer\n info: { channels: number }\n }>\n}\ntype SharpPixels = (input: Buffer, options?: { limitInputPixels?: number }) => SharpPixelPipeline\n\nconst LOGO_TYPES = /^image\\/(png|jpeg|webp|gif|avif|svg\\+xml)$/\n\n/**\n * A library logo's colors, read the way the media CDN would serve it\n * (`readAiMediaAssetBytes`). `null` when the asset may not be read or the bytes\n * cannot be decoded.\n */\nasync function readLogoColors(\n firestore: FirebaseFirestore.Firestore,\n location: AiThemeLogoLocation,\n hostId: string,\n): Promise<string[] | null> {\n const asset = await readAiMediaAssetBytes(firestore, location, hostId, {\n maxBytes: AI_THEME_LOGO_MAX_BYTES,\n types: LOGO_TYPES,\n })\n if (!asset) return null\n const sharp = (await loadAiMediaSharp()) as SharpPixels\n const { data, info } = await sharp(asset.buffer, { limitInputPixels: 64_000_000 })\n .resize(48, 48, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n return dominantColorsFromPixels(data, info.channels)\n}\n\n/* ------------------------------------------------------------------------ *\n * A page the brief links to\n * ------------------------------------------------------------------------ */\n\nconst URL_IN_BRIEF = /\\bhttps?:\\/\\/[^\\s<>\"'`)\\]]+/i\n\n/** The first https link in a brief, or `null` — an http link is not followed. */\nexport function referenceUrlOf(brief: string): URL | null {\n const match = URL_IN_BRIEF.exec(brief)\n if (!match) return null\n try {\n const url = new URL(match[0].replace(/[.,;:!?]+$/, ''))\n return url.protocol === 'https:' ? url : null\n } catch {\n return null\n }\n}\n\n/** How much a page's declared `theme-color` counts beside one use of a color. */\nexport const AI_THEME_THEME_COLOR_WEIGHT = 25\n\nconst THEME_COLOR_META = /<meta\\b[^>]*\\bname\\s*=\\s*[\"']?theme-color\\b[^>]*>/gi\nconst CONTENT_ATTRIBUTE = /\\bcontent\\s*=\\s*[\"']?\\s*([^\"'\\s>]+)/i\n// A `#` after `&` is a character reference (`'`), not a color.\nconst HEX_LITERAL = /(?<!&)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?![0-9a-z])/gi\nconst RGB_LITERAL = /rgba?\\(\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})/gi\n\n/** Every color a page's markup or a stylesheet uses, weighted by how often. */\nexport function colorsFromMarkup(text: string): Map<string, number> {\n const weights = new Map<string, number>()\n const add = (hex: string | null, weight: number) => {\n if (hex) weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n for (const tag of text.match(THEME_COLOR_META) ?? []) {\n add(normalizeThemeHex(CONTENT_ATTRIBUTE.exec(tag)?.[1] ?? ''), AI_THEME_THEME_COLOR_WEIGHT)\n }\n for (const match of text.matchAll(HEX_LITERAL)) add(normalizeThemeHex(match[0]), 1)\n for (const match of text.matchAll(RGB_LITERAL)) {\n add(toHex([Number(match[1]), Number(match[2]), Number(match[3])]), 1)\n }\n return weights\n}\n\nconst STYLESHEET_LINK = /<link\\b[^>]*\\brel\\s*=\\s*[\"']?stylesheet\\b[^>]*>/gi\nconst HREF_ATTRIBUTE = /\\bhref\\s*=\\s*[\"']?\\s*([^\"'\\s>]+)/i\n\n/** The https stylesheets a page links, resolved against it, at most a few. */\nexport function stylesheetUrlsOf(html: string, pageUrl: string): string[] {\n const urls: string[] = []\n for (const tag of html.match(STYLESHEET_LINK) ?? []) {\n const href = HREF_ATTRIBUTE.exec(tag)?.[1]\n if (!href) continue\n try {\n const url = new URL(href, pageUrl)\n if (url.protocol === 'https:' && !urls.includes(url.toString())) urls.push(url.toString())\n } catch {\n continue\n }\n if (urls.length === AI_THEME_REFERENCE_STYLESHEETS) break\n }\n return urls\n}\n\n/** The most used distinct colors, the colorful before the greys. */\nexport function rankBrandColors(\n weights: ReadonlyMap<string, number>,\n count = AI_THEME_BRAND_COLORS_PER_SOURCE,\n): string[] {\n const ranked = [...weights.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([hex]) => channelsOf(hex))\n return distinctColors(ranked, count)\n}\n\n/** One abort for the caller's signal and a clock, whichever ends first. */\nfunction deadline(signal: AbortSignal | undefined, ms: number) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), ms)\n const onAbort = () => controller.abort()\n if (signal?.aborted) controller.abort()\n signal?.addEventListener('abort', onAbort)\n return {\n signal: controller.signal,\n done: () => {\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n },\n }\n}\n\nasync function readCapped(body: ReadableStream<Uint8Array>, maxBytes: number): Promise<string> {\n const reader = body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done || !value) break\n const room = maxBytes - total\n if (value.byteLength >= room) {\n chunks.push(value.subarray(0, room))\n await reader.cancel().catch(() => undefined)\n break\n }\n chunks.push(value)\n total += value.byteLength\n }\n const joined = new Uint8Array(chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0))\n let offset = 0\n for (const chunk of chunks) {\n joined.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(joined)\n}\n\n/**\n * A public https resource as text, through the plugin fetch proxy's SSRF\n * guard: the host must resolve only to public addresses and the socket is\n * pinned to the address checked; no credentials in the URL and no port but\n * the default; every redirect re-enters the same checks; the body is read to\n * a byte cap under a clock, and only a content type the caller accepts is\n * read at all. `null` for anything that does not hold.\n */\nexport async function fetchPublicText(\n url: string,\n options: { maxBytes: number; accept: RegExp; signal?: AbortSignal; timeoutMs?: number },\n): Promise<{ text: string; url: string } | null> {\n const { createPinnedDispatcher, resolvePublicIp } = await import('./ai-theme-brand-server')\n let current = url\n for (let hop = 0; hop <= MAX_REDIRECT_HOPS; hop += 1) {\n let parsed: URL\n try {\n parsed = new URL(current)\n } catch {\n return null\n }\n if (parsed.protocol !== 'https:' || parsed.username || parsed.password) return null\n if (parsed.port && parsed.port !== '443') return null\n const pinned = await resolvePublicIp(parsed.hostname)\n if (!pinned) return null\n const dispatcher = createPinnedDispatcher(pinned)\n const clock = deadline(options.signal, options.timeoutMs ?? FETCH_TIMEOUT_MS)\n try {\n // `dispatcher` is undici's, which the global fetch honors; a variable\n // rather than a literal keeps the DOM `RequestInit` check off it.\n const init = {\n method: 'GET',\n redirect: 'manual' as const,\n signal: clock.signal,\n dispatcher,\n headers: { Accept: 'text/html, text/css;q=0.9' },\n }\n const response = await fetch(parsed.toString(), init as RequestInit)\n if (REDIRECT_STATUSES.has(response.status)) {\n const location = response.headers.get('location')\n if (!location) return null\n current = new URL(location, parsed).toString()\n continue\n }\n if (!response.ok || !response.body) return null\n if (!options.accept.test(response.headers.get('content-type') ?? '')) return null\n return { text: await readCapped(response.body, options.maxBytes), url: parsed.toString() }\n } catch {\n return null\n } finally {\n clock.done()\n await dispatcher.close().catch(() => undefined)\n }\n }\n return null\n}\n\n/** A linked page's colors, from its markup and its first stylesheets. */\nasync function readReferenceColors(url: URL, signal: AbortSignal): Promise<string[] | null> {\n const page = await fetchPublicText(url.toString(), {\n maxBytes: AI_THEME_REFERENCE_MAX_BYTES,\n accept: /text\\/html|application\\/xhtml\\+xml/i,\n signal,\n })\n if (!page) return null\n const weights = colorsFromMarkup(page.text)\n for (const sheet of stylesheetUrlsOf(page.text, page.url)) {\n const css = await fetchPublicText(sheet, {\n maxBytes: AI_THEME_STYLESHEET_MAX_BYTES,\n accept: /text\\/css/i,\n signal,\n })\n if (!css) continue\n for (const [hex, weight] of colorsFromMarkup(css.text)) {\n weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n }\n return rankBrandColors(weights)\n}\n\n/* ------------------------------------------------------------------------ *\n * Gathering\n * ------------------------------------------------------------------------ */\n\n/** The two reads that leave the process, replaceable where a spec needs to. */\nexport interface AiThemeBrandSeams {\n logoColors?: (\n firestore: FirebaseFirestore.Firestore,\n location: AiThemeLogoLocation,\n hostId: string,\n ) => Promise<string[] | null>\n referenceColors?: (url: URL, signal: AbortSignal) => Promise<string[] | null>\n}\n\nexport async function gatherAiThemeBrandInputs(\n input: {\n firestore: FirebaseFirestore.Firestore\n org: Partial<AglynOrgBilling> | null\n hostId: string\n /** The site's host document. */\n host: Record<string, unknown>\n brief: string\n signal?: AbortSignal\n },\n seams: AiThemeBrandSeams = {},\n): Promise<AiThemeBrandInputs> {\n const colors: AiThemeBrandColor[] = [...organizationBrandColors(input.org)]\n const notes: string[] = []\n const clock = deadline(input.signal, AI_THEME_BRAND_BUDGET_MS)\n const add = (found: readonly string[], source: AiThemeBrandSource) => {\n for (const hex of found.slice(0, AI_THEME_BRAND_COLORS_PER_SOURCE)) {\n if (!colors.some((color) => color.hex === hex)) colors.push({ hex, source })\n }\n }\n try {\n const location = logoMediaLocation(\n input.host['logoUrl'],\n input.hostId,\n String(input.host['orgId'] ?? ''),\n )\n if (location) {\n const found = await (seams.logoColors ?? readLogoColors)(\n input.firestore,\n location,\n input.hostId,\n ).catch(() => null)\n if (found) add(found, 'logo')\n else notes.push(\"The site logo's colors could not be read, so the proposal does not use them.\")\n }\n const reference = referenceUrlOf(input.brief)\n if (reference) {\n const found = clock.signal.aborted\n ? null\n : await (seams.referenceColors ?? readReferenceColors)(reference, clock.signal).catch(\n () => null,\n )\n if (found?.length) add(found, 'reference')\n else {\n notes.push(\n `The page at ${reference.hostname} could not be read for its colors, so the proposal does not use them.`,\n )\n }\n }\n } finally {\n clock.done()\n }\n return { colors, notes }\n}\n"],"names":["checkEntitlement","normalizeThemeHex","AI_THEME_BRAND_BUDGET_MS","aiMediaAssetLocation","loadAiMediaSharp","readAiMediaAssetBytes","AI_THEME_BRAND_SOURCE_WORDS","organization","logo","reference","AI_THEME_BRAND_COLORS_PER_SOURCE","AI_THEME_LOGO_MAX_BYTES","AI_THEME_REFERENCE_MAX_BYTES","AI_THEME_STYLESHEET_MAX_BYTES","AI_THEME_REFERENCE_STYLESHEETS","FETCH_TIMEOUT_MS","MAX_REDIRECT_HOPS","REDIRECT_STATUSES","Set","toHex","red","green","blue","map","channel","Math","round","min","max","toString","padStart","join","channelsOf","hex","parseInt","slice","distance","a","b","hypot","chroma","SAME_COLOR_DISTANCE","GREY_CHROMA","distinctColors","ranked","count","colorful","filter","color","chosen","length","some","other","push","organizationBrandColors","org","raw","brandingProfile","primaryColor","source","logoMediaLocation","dominantColorsFromPixels","data","channels","buckets","Map","index","pixel","key","bucket","get","sum","pixels","set","values","sort","edge","inside","LOGO_TYPES","readLogoColors","firestore","location","hostId","asset","maxBytes","types","sharp","info","buffer","limitInputPixels","resize","fit","ensureAlpha","toBuffer","resolveWithObject","URL_IN_BRIEF","referenceUrlOf","brief","match","exec","url","URL","replace","protocol","AI_THEME_THEME_COLOR_WEIGHT","THEME_COLOR_META","CONTENT_ATTRIBUTE","HEX_LITERAL","RGB_LITERAL","colorsFromMarkup","text","weights","add","weight","tag","matchAll","Number","STYLESHEET_LINK","HREF_ATTRIBUTE","stylesheetUrlsOf","html","pageUrl","urls","href","includes","rankBrandColors","entries","deadline","signal","ms","controller","AbortController","timer","setTimeout","abort","onAbort","aborted","addEventListener","done","clearTimeout","removeEventListener","readCapped","body","reader","getReader","chunks","total","value","read","room","byteLength","subarray","cancel","catch","undefined","joined","Uint8Array","reduce","chunk","offset","TextDecoder","decode","fetchPublicText","options","createPinnedDispatcher","resolvePublicIp","current","hop","parsed","username","password","port","pinned","hostname","dispatcher","clock","timeoutMs","response","init","method","redirect","headers","Accept","fetch","has","status","ok","accept","test","close","readReferenceColors","page","sheet","css","gatherAiThemeBrandInputs","input","seams","colors","notes","found","host","String","logoColors","referenceColors"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,2CAA0C;AAK3E,SAASC,iBAAiB,QAAQ,4BAAwB;AAC1D,SAASC,wBAAwB,QAAQ,2BAAuB;AAChE,SACEC,oBAAoB,EACpBC,gBAAgB,EAChBC,qBAAqB,QAEhB,sBAAkB;AA0BzB,2DAA2D,GAC3D,OAAO,MAAMC,8BAAkE;IAC7EC,cAAc;IACdC,MAAM;IACNC,WAAW;AACb,EAAC;AAaD,4CAA4C,GAC5C,OAAO,MAAMC,mCAAmC,EAAC;AAEjD,6CAA6C,GAC7C,OAAO,MAAMC,0BAA0B,IAAI,OAAO,KAAI;AAEtD,6EAA6E,GAC7E,OAAO,MAAMC,+BAA+B,MAAM,KAAI;AACtD,OAAO,MAAMC,gCAAgC,MAAM,KAAI;AAEvD,8DAA8D,GAC9D,OAAO,MAAMC,iCAAiC,EAAC;AAE/C;;;CAGC,GACD,SAASZ,wBAAwB,GAAE;AAEnC,MAAMa,mBAAmB;AACzB,MAAMC,oBAAoB;AAC1B,MAAMC,oBAAoB,IAAIC,IAAI;IAAC;IAAK;IAAK;IAAK;IAAK;CAAI;AAI3D,MAAMC,QAAQ,CAAC,CAACC,KAAKC,OAAOC,KAAU,GACpC,CAAC,CAAC,EAAE;QAACF;QAAKC;QAAOC;KAAK,CACnBC,GAAG,CAAC,CAACC,UAAYC,KAAKC,KAAK,CAACD,KAAKE,GAAG,CAAC,KAAKF,KAAKG,GAAG,CAAC,GAAGJ,WAAWK,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAC1FC,IAAI,CAAC,KAAK;AAEf,MAAMC,aAAa,CAACC,MAAqB;QACvCC,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;QAC1BD,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;QAC1BD,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;KAC3B;AAED,MAAMC,WAAW,CAACC,GAAQC,IAAWb,KAAKc,KAAK,CAACF,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE,EAAED,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE,EAAED,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE;AAErF,8DAA8D,GAC9D,MAAME,SAAS,CAAC,CAACpB,KAAKC,OAAOC,KAAU,GAAKG,KAAKG,GAAG,CAACR,KAAKC,OAAOC,QAAQG,KAAKE,GAAG,CAACP,KAAKC,OAAOC;AAE9F,iEAAiE,GACjE,MAAMmB,sBAAsB;AAE5B,0EAA0E,GAC1E,MAAMC,cAAc;AAEpB,kFAAkF,GAClF,SAASC,eAAeC,MAAsB,EAAEC,KAAa;IAC3D,MAAMC,WAAWF,OAAOG,MAAM,CAAC,CAACC,QAAUR,OAAOQ,SAASN;IAC1D,MAAMO,SAAgB,EAAE;IACxB,KAAK,MAAMD,SAASF,SAASI,MAAM,GAAGJ,WAAWF,OAAQ;QACvD,IAAIK,OAAOE,IAAI,CAAC,CAACC,QAAUhB,SAASgB,OAAOJ,SAASP,sBAAsB;QAC1EQ,OAAOI,IAAI,CAACL;QACZ,IAAIC,OAAOC,MAAM,KAAKL,OAAO;IAC/B;IACA,OAAOI,OAAO1B,GAAG,CAACJ;AACpB;AAEA;;4EAE4E,GAE5E,gFAAgF,GAChF,OAAO,SAASmC,wBACdC,GAAgD;QAGnCA;IADb,IAAI,CAACvD,iBAAiBuD,KAAK,eAAe,OAAO,EAAE;IACnD,MAAMC,MAAOD,wBAAAA,uBAAAA,IAAKE,eAAe,qBAArB,AAACF,qBAAyDG,YAAY;IAClF,MAAMzB,MAAM,OAAOuB,QAAQ,WAAWvD,kBAAkBuD,OAAO;IAC/D,OAAOvB,MAAM;QAAC;YAAEA;YAAK0B,QAAQ;QAAe;KAAE,GAAG,EAAE;AACrD;AAQA;;;;;;CAMC,GACD,OAAO,MAAMC,oBAAoBzD,qBAAoB;AAErD;;;;;CAKC,GACD,OAAO,SAAS0D,yBACdC,IAAuB,EACvBC,QAAgB,EAChBlB,QAAQnC,gCAAgC;IAExC,MAAMsD,UAAU,IAAIC;IACpB,IAAK,IAAIC,QAAQ,GAAGA,QAAQH,YAAYD,KAAKZ,MAAM,EAAEgB,SAASH,SAAU;YAIvDC;QAHf,IAAID,YAAY,KAAKD,IAAI,CAACI,QAAQ,EAAE,GAAG,KAAK;QAC5C,MAAMC,QAAa;YAACL,IAAI,CAACI,MAAM;YAAEJ,IAAI,CAACI,QAAQ,EAAE;YAAEJ,IAAI,CAACI,QAAQ,EAAE;SAAC;QAClE,MAAME,MAAM,AAAED,KAAK,CAAC,EAAE,IAAI,KAAM,IAAM,AAACA,KAAK,CAAC,EAAE,IAAI,KAAM,IAAMA,KAAK,CAAC,EAAE,IAAI;QAC3E,MAAME,UAASL,eAAAA,QAAQM,GAAG,CAACF,gBAAZJ,eAAoB;YAAEO,KAAK;gBAAC;gBAAG;gBAAG;aAAE;YAASC,QAAQ;QAAE;QACtEH,OAAOE,GAAG,GAAG;YAACF,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;YAAEE,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;YAAEE,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;SAAC;QAC3FE,OAAOG,MAAM,IAAI;QACjBR,QAAQS,GAAG,CAACL,KAAKC;IACnB;IACA,MAAMzB,SAAS;WAAIoB,QAAQU,MAAM;KAAG,CACjCC,IAAI,CAAC,CAACtC,GAAGC,IAAMA,EAAEkC,MAAM,GAAGnC,EAAEmC,MAAM,EAClCjD,GAAG,CAAC,CAAC,EAAEgD,GAAG,EAAEC,MAAM,EAAE,GAAU;YAACD,GAAG,CAAC,EAAE,GAAGC;YAAQD,GAAG,CAAC,EAAE,GAAGC;YAAQD,GAAG,CAAC,EAAE,GAAGC;SAAO;IACpF,MAAMI,OAAO,CAAC5B,QAAevB,KAAKE,GAAG,IAAIqB,SAAS,OAAOvB,KAAKG,GAAG,IAAIoB,SAAS;IAC9E,MAAM6B,SAASjC,OAAOG,MAAM,CAAC,CAACC,QAAU,CAAC4B,KAAK5B;IAC9C,OAAOL,eAAekC,OAAO3B,MAAM,GAAG2B,SAASjC,QAAQC;AACzD;AAcA,MAAMiC,aAAa;AAEnB;;;;CAIC,GACD,eAAeC,eACbC,SAAsC,EACtCC,QAA6B,EAC7BC,MAAc;IAEd,MAAMC,QAAQ,MAAM9E,sBAAsB2E,WAAWC,UAAUC,QAAQ;QACrEE,UAAUzE;QACV0E,OAAOP;IACT;IACA,IAAI,CAACK,OAAO,OAAO;IACnB,MAAMG,QAAS,MAAMlF;IACrB,MAAM,EAAE0D,IAAI,EAAEyB,IAAI,EAAE,GAAG,MAAMD,MAAMH,MAAMK,MAAM,EAAE;QAAEC,kBAAkB;IAAW,GAC7EC,MAAM,CAAC,IAAI,IAAI;QAAEC,KAAK;IAAS,GAC/BC,WAAW,GACXpC,GAAG,GACHqC,QAAQ,CAAC;QAAEC,mBAAmB;IAAK;IACtC,OAAOjC,yBAAyBC,MAAMyB,KAAKxB,QAAQ;AACrD;AAEA;;4EAE4E,GAE5E,MAAMgC,eAAe;AAErB,+EAA+E,GAC/E,OAAO,SAASC,eAAeC,KAAa;IAC1C,MAAMC,QAAQH,aAAaI,IAAI,CAACF;IAChC,IAAI,CAACC,OAAO,OAAO;IACnB,IAAI;QACF,MAAME,MAAM,IAAIC,IAAIH,KAAK,CAAC,EAAE,CAACI,OAAO,CAAC,cAAc;QACnD,OAAOF,IAAIG,QAAQ,KAAK,WAAWH,MAAM;IAC3C,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA,+EAA+E,GAC/E,OAAO,MAAMI,8BAA8B,GAAE;AAE7C,MAAMC,mBAAmB;AACzB,MAAMC,oBAAoB;AAC1B,oEAAoE;AACpE,MAAMC,cAAc;AACpB,MAAMC,cAAc;AAEpB,6EAA6E,GAC7E,OAAO,SAASC,iBAAiBC,IAAY;QAKzBA;IAJlB,MAAMC,UAAU,IAAI9C;IACpB,MAAM+C,MAAM,CAAC/E,KAAoBgF;YACJF;QAA3B,IAAI9E,KAAK8E,QAAQtC,GAAG,CAACxC,KAAK,EAAC8E,eAAAA,QAAQzC,GAAG,CAACrC,gBAAZ8E,eAAoB,KAAKE;IACtD;IACA,KAAK,MAAMC,QAAOJ,cAAAA,KAAKZ,KAAK,CAACO,6BAAXK,cAAgC,EAAE,CAAE;;YAC9BJ;QAAtBM,IAAI/G,2BAAkByG,0BAAAA,kBAAkBP,IAAI,CAACe,yBAAvBR,uBAA6B,CAAC,EAAE,mBAAI,KAAKF;IACjE;IACA,KAAK,MAAMN,SAASY,KAAKK,QAAQ,CAACR,aAAcK,IAAI/G,kBAAkBiG,KAAK,CAAC,EAAE,GAAG;IACjF,KAAK,MAAMA,SAASY,KAAKK,QAAQ,CAACP,aAAc;QAC9CI,IAAI7F,MAAM;YAACiG,OAAOlB,KAAK,CAAC,EAAE;YAAGkB,OAAOlB,KAAK,CAAC,EAAE;YAAGkB,OAAOlB,KAAK,CAAC,EAAE;SAAE,GAAG;IACrE;IACA,OAAOa;AACT;AAEA,MAAMM,kBAAkB;AACxB,MAAMC,iBAAiB;AAEvB,4EAA4E,GAC5E,OAAO,SAASC,iBAAiBC,IAAY,EAAEC,OAAe;QAE1CD;IADlB,MAAME,OAAiB,EAAE;IACzB,KAAK,MAAMR,QAAOM,cAAAA,KAAKtB,KAAK,CAACmB,4BAAXG,cAA+B,EAAE,CAAE;YACtCF;QAAb,MAAMK,QAAOL,uBAAAA,eAAenB,IAAI,CAACe,yBAApBI,oBAA0B,CAAC,EAAE;QAC1C,IAAI,CAACK,MAAM;QACX,IAAI;YACF,MAAMvB,MAAM,IAAIC,IAAIsB,MAAMF;YAC1B,IAAIrB,IAAIG,QAAQ,KAAK,YAAY,CAACmB,KAAKE,QAAQ,CAACxB,IAAIvE,QAAQ,KAAK6F,KAAKrE,IAAI,CAAC+C,IAAIvE,QAAQ;QACzF,EAAE,eAAM;YACN;QACF;QACA,IAAI6F,KAAKxE,MAAM,KAAKpC,gCAAgC;IACtD;IACA,OAAO4G;AACT;AAEA,kEAAkE,GAClE,OAAO,SAASG,gBACdd,OAAoC,EACpClE,QAAQnC,gCAAgC;IAExC,MAAMkC,SAAS;WAAImE,QAAQe,OAAO;KAAG,CAClCnD,IAAI,CAAC,CAACtC,GAAGC,IAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE,EAC1Bd,GAAG,CAAC,CAAC,CAACU,IAAI,GAAKD,WAAWC;IAC7B,OAAOU,eAAeC,QAAQC;AAChC;AAEA,yEAAyE,GACzE,SAASkF,SAASC,MAA+B,EAAEC,EAAU;IAC3D,MAAMC,aAAa,IAAIC;IACvB,MAAMC,QAAQC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IACnD,MAAMM,UAAU,IAAML,WAAWI,KAAK;IACtC,IAAIN,0BAAAA,OAAQQ,OAAO,EAAEN,WAAWI,KAAK;IACrCN,0BAAAA,OAAQS,gBAAgB,CAAC,SAASF;IAClC,OAAO;QACLP,QAAQE,WAAWF,MAAM;QACzBU,MAAM;YACJC,aAAaP;YACbJ,0BAAAA,OAAQY,mBAAmB,CAAC,SAASL;QACvC;IACF;AACF;AAEA,eAAeM,WAAWC,IAAgC,EAAE1D,QAAgB;IAC1E,MAAM2D,SAASD,KAAKE,SAAS;IAC7B,MAAMC,SAAuB,EAAE;IAC/B,IAAIC,QAAQ;IACZ,OAAS;QACP,MAAM,EAAER,IAAI,EAAES,KAAK,EAAE,GAAG,MAAMJ,OAAOK,IAAI;QACzC,IAAIV,QAAQ,CAACS,OAAO;QACpB,MAAME,OAAOjE,WAAW8D;QACxB,IAAIC,MAAMG,UAAU,IAAID,MAAM;YAC5BJ,OAAO5F,IAAI,CAAC8F,MAAMI,QAAQ,CAAC,GAAGF;YAC9B,MAAMN,OAAOS,MAAM,GAAGC,KAAK,CAAC,IAAMC;YAClC;QACF;QACAT,OAAO5F,IAAI,CAAC8F;QACZD,SAASC,MAAMG,UAAU;IAC3B;IACA,MAAMK,SAAS,IAAIC,WAAWX,OAAOY,MAAM,CAAC,CAACtF,KAAKuF,QAAUvF,MAAMuF,MAAMR,UAAU,EAAE;IACpF,IAAIS,SAAS;IACb,KAAK,MAAMD,SAASb,OAAQ;QAC1BU,OAAOlF,GAAG,CAACqF,OAAOC;QAClBA,UAAUD,MAAMR,UAAU;IAC5B;IACA,OAAO,IAAIU,cAAcC,MAAM,CAACN;AAClC;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeO,gBACpB9D,GAAW,EACX+D,OAAuF;IAEvF,MAAM,EAAEC,sBAAsB,EAAEC,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;IACjE,IAAIC,UAAUlE;IACd,IAAK,IAAImE,MAAM,GAAGA,OAAOvJ,mBAAmBuJ,OAAO,EAAG;YAYbJ;QAXvC,IAAIK;QACJ,IAAI;YACFA,SAAS,IAAInE,IAAIiE;QACnB,EAAE,eAAM;YACN,OAAO;QACT;QACA,IAAIE,OAAOjE,QAAQ,KAAK,YAAYiE,OAAOC,QAAQ,IAAID,OAAOE,QAAQ,EAAE,OAAO;QAC/E,IAAIF,OAAOG,IAAI,IAAIH,OAAOG,IAAI,KAAK,OAAO,OAAO;QACjD,MAAMC,SAAS,MAAMP,gBAAgBG,OAAOK,QAAQ;QACpD,IAAI,CAACD,QAAQ,OAAO;QACpB,MAAME,aAAaV,uBAAuBQ;QAC1C,MAAMG,QAAQhD,SAASoC,QAAQnC,MAAM,GAAEmC,qBAAAA,QAAQa,SAAS,YAAjBb,qBAAqBpJ;QAC5D,IAAI;gBAkBuBkK;YAjBzB,sEAAsE;YACtE,kEAAkE;YAClE,MAAMC,OAAO;gBACXC,QAAQ;gBACRC,UAAU;gBACVpD,QAAQ+C,MAAM/C,MAAM;gBACpB8C;gBACAO,SAAS;oBAAEC,QAAQ;gBAA4B;YACjD;YACA,MAAML,WAAW,MAAMM,MAAMf,OAAO3I,QAAQ,IAAIqJ;YAChD,IAAIjK,kBAAkBuK,GAAG,CAACP,SAASQ,MAAM,GAAG;gBAC1C,MAAMxG,WAAWgG,SAASI,OAAO,CAAC/G,GAAG,CAAC;gBACtC,IAAI,CAACW,UAAU,OAAO;gBACtBqF,UAAU,IAAIjE,IAAIpB,UAAUuF,QAAQ3I,QAAQ;gBAC5C;YACF;YACA,IAAI,CAACoJ,SAASS,EAAE,IAAI,CAACT,SAASnC,IAAI,EAAE,OAAO;YAC3C,IAAI,CAACqB,QAAQwB,MAAM,CAACC,IAAI,EAACX,wBAAAA,SAASI,OAAO,CAAC/G,GAAG,CAAC,2BAArB2G,wBAAwC,KAAK,OAAO;YAC7E,OAAO;gBAAEnE,MAAM,MAAM+B,WAAWoC,SAASnC,IAAI,EAAEqB,QAAQ/E,QAAQ;gBAAGgB,KAAKoE,OAAO3I,QAAQ;YAAG;QAC3F,EAAE,eAAM;YACN,OAAO;QACT,SAAU;YACRkJ,MAAMrC,IAAI;YACV,MAAMoC,WAAWe,KAAK,GAAGpC,KAAK,CAAC,IAAMC;QACvC;IACF;IACA,OAAO;AACT;AAEA,uEAAuE,GACvE,eAAeoC,oBAAoB1F,GAAQ,EAAE4B,MAAmB;IAC9D,MAAM+D,OAAO,MAAM7B,gBAAgB9D,IAAIvE,QAAQ,IAAI;QACjDuD,UAAUxE;QACV+K,QAAQ;QACR3D;IACF;IACA,IAAI,CAAC+D,MAAM,OAAO;IAClB,MAAMhF,UAAUF,iBAAiBkF,KAAKjF,IAAI;IAC1C,KAAK,MAAMkF,SAASzE,iBAAiBwE,KAAKjF,IAAI,EAAEiF,KAAK3F,GAAG,EAAG;QACzD,MAAM6F,MAAM,MAAM/B,gBAAgB8B,OAAO;YACvC5G,UAAUvE;YACV8K,QAAQ;YACR3D;QACF;QACA,IAAI,CAACiE,KAAK;QACV,KAAK,MAAM,CAAChK,KAAKgF,OAAO,IAAIJ,iBAAiBoF,IAAInF,IAAI,EAAG;gBACpCC;YAAlBA,QAAQtC,GAAG,CAACxC,KAAK,EAAC8E,eAAAA,QAAQzC,GAAG,CAACrC,gBAAZ8E,eAAoB,KAAKE;QAC7C;IACF;IACA,OAAOY,gBAAgBd;AACzB;AAgBA,OAAO,eAAemF,yBACpBC,KAQC,EACDC,QAA2B,CAAC,CAAC;IAE7B,MAAMC,SAA8B;WAAI/I,wBAAwB6I,MAAM5I,GAAG;KAAE;IAC3E,MAAM+I,QAAkB,EAAE;IAC1B,MAAMvB,QAAQhD,SAASoE,MAAMnE,MAAM,EAAE9H;IACrC,MAAM8G,MAAM,CAACuF,OAA0B5I;QACrC,KAAK,MAAM1B,OAAOsK,MAAMpK,KAAK,CAAC,GAAGzB,kCAAmC;YAClE,IAAI,CAAC2L,OAAOlJ,IAAI,CAAC,CAACH,QAAUA,MAAMf,GAAG,KAAKA,MAAMoK,OAAOhJ,IAAI,CAAC;gBAAEpB;gBAAK0B;YAAO;QAC5E;IACF;IACA,IAAI;YAIOwI;QAHT,MAAMlH,WAAWrB,kBACfuI,MAAMK,IAAI,CAAC,UAAU,EACrBL,MAAMjH,MAAM,EACZuH,QAAON,oBAAAA,MAAMK,IAAI,CAAC,QAAQ,YAAnBL,oBAAuB;QAEhC,IAAIlH,UAAU;gBACSmH;YAArB,MAAMG,QAAQ,MAAM,EAACH,oBAAAA,MAAMM,UAAU,YAAhBN,oBAAoBrH,gBACvCoH,MAAMnH,SAAS,EACfC,UACAkH,MAAMjH,MAAM,EACZuE,KAAK,CAAC,IAAM;YACd,IAAI8C,OAAOvF,IAAIuF,OAAO;iBACjBD,MAAMjJ,IAAI,CAAC;QAClB;QACA,MAAM5C,YAAYuF,eAAemG,MAAMlG,KAAK;QAC5C,IAAIxF,WAAW;gBAGF2L;YAFX,MAAMG,QAAQxB,MAAM/C,MAAM,CAACQ,OAAO,GAC9B,OACA,MAAM,EAAC4D,yBAAAA,MAAMO,eAAe,YAArBP,yBAAyBN,qBAAqBrL,WAAWsK,MAAM/C,MAAM,EAAEyB,KAAK,CACjF,IAAM;YAEZ,IAAI8C,yBAAAA,MAAOrJ,MAAM,EAAE8D,IAAIuF,OAAO;iBACzB;gBACHD,MAAMjJ,IAAI,CACR,CAAC,YAAY,EAAE5C,UAAUoK,QAAQ,CAAC,qEAAqE,CAAC;YAE5G;QACF;IACF,SAAU;QACRE,MAAMrC,IAAI;IACZ;IACA,OAAO;QAAE2D;QAAQC;IAAM;AACzB"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-theme-brand-inputs.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 { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type {\n AglynOrgBilling,\n OrgBrandingProfile,\n} from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { normalizeThemeHex } from '../tools/ai-theme-tool'\nimport { AI_THEME_BRAND_BUDGET_MS } from './ai-job-theme-budget'\nimport {\n aiMediaAssetLocation,\n loadAiMediaSharp,\n readAiMediaAssetBytes,\n type AiMediaAssetLocation,\n} from './ai-media-asset'\n\n/**\n * The brand a theme job builds on (AGL-2938): the colors that belong to the\n * site before anyone describes them.\n *\n * - **The workspace's brand color** — a white-label workspace's, and only\n * theirs: without the entitlement the resolved profile is the platform's,\n * which is no customer's brand.\n * - **The site logo** — when the logo is an asset of the site's own media\n * library or its org's, read from storage and reduced to its dominant\n * colors. Never fetched from a URL: a logo that is not a library asset is\n * left alone.\n * - **A page the brief links to** — fetched through the same SSRF guard the\n * plugin fetch proxy uses (a public address, pinned; https only; every\n * redirect re-validated), capped in bytes and time, and reduced to the\n * colors its markup and stylesheets use.\n *\n * Only hex colors leave this module. Nothing a page or a logo SAYS reaches a\n * prompt, so there is nothing in them to follow. Each source is best effort:\n * one that cannot be read is named in a note and the proposal is made\n * without it.\n */\n\nexport type AiThemeBrandSource = 'organization' | 'logo' | 'reference'\n\n/** How the prompt names where each brand color came from. */\nexport const AI_THEME_BRAND_SOURCE_WORDS: Record<AiThemeBrandSource, string> = {\n organization: 'the workspace brand color',\n logo: 'from the site logo',\n reference: 'from the page the brief links to',\n}\n\nexport interface AiThemeBrandColor {\n hex: string\n source: AiThemeBrandSource\n}\n\nexport interface AiThemeBrandInputs {\n colors: AiThemeBrandColor[]\n /** A source that could not be read, in customer-safe words. */\n notes: string[]\n}\n\n/** The most colors one source contributes. */\nexport const AI_THEME_BRAND_COLORS_PER_SOURCE = 4\n\n/** The largest logo decoded for its colors. */\nexport const AI_THEME_LOGO_MAX_BYTES = 5 * 1024 * 1024\n\n/** The most of a linked page, and of each of its stylesheets, that is read. */\nexport const AI_THEME_REFERENCE_MAX_BYTES = 512 * 1024\nexport const AI_THEME_STYLESHEET_MAX_BYTES = 256 * 1024\n\n/** The stylesheets of a linked page that are read beside it. */\nexport const AI_THEME_REFERENCE_STYLESHEETS = 2\n\n/**\n * The wall clock brand colors may take, declared with the theme step's time\n * (`ai-job-theme-budget.ts`), which counts it (AGL-3035).\n */\nexport { AI_THEME_BRAND_BUDGET_MS }\n\nconst FETCH_TIMEOUT_MS = 4_000\nconst MAX_REDIRECT_HOPS = 3\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308])\n\ntype Rgb = [red: number, green: number, blue: number]\n\nconst toHex = ([red, green, blue]: Rgb) =>\n `#${[red, green, blue]\n .map((channel) => Math.round(Math.min(255, Math.max(0, channel))).toString(16).padStart(2, '0'))\n .join('')}`\n\nconst channelsOf = (hex: string): Rgb => [\n parseInt(hex.slice(1, 3), 16),\n parseInt(hex.slice(3, 5), 16),\n parseInt(hex.slice(5, 7), 16),\n]\n\nconst distance = (a: Rgb, b: Rgb) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2])\n\n/** How far a color is from grey: the spread of its channels. */\nconst chroma = ([red, green, blue]: Rgb) => Math.max(red, green, blue) - Math.min(red, green, blue)\n\n/** Colors closer than this read as the same color in a palette. */\nconst SAME_COLOR_DISTANCE = 40\n\n/** A spread under this reads as a grey, which says little about a brand. */\nconst GREY_CHROMA = 38\n\n/** The distinct colors of a ranked list, colorful ones first when there are any. */\nfunction distinctColors(ranked: readonly Rgb[], count: number): string[] {\n const colorful = ranked.filter((color) => chroma(color) > GREY_CHROMA)\n const chosen: Rgb[] = []\n for (const color of colorful.length ? colorful : ranked) {\n if (chosen.some((other) => distance(other, color) < SAME_COLOR_DISTANCE)) continue\n chosen.push(color)\n if (chosen.length === count) break\n }\n return chosen.map(toHex)\n}\n\n/* ------------------------------------------------------------------------ *\n * The workspace's brand color\n * ------------------------------------------------------------------------ */\n\n/** A white-label workspace's own brand color; nothing for any other workspace. */\nexport function organizationBrandColors(\n org: Partial<AglynOrgBilling> | null | undefined,\n): AiThemeBrandColor[] {\n if (!checkEntitlement(org, 'whiteLabel')) return []\n const raw = (org?.brandingProfile as OrgBrandingProfile | undefined)?.primaryColor\n const hex = typeof raw === 'string' ? normalizeThemeHex(raw) : null\n return hex ? [{ hex, source: 'organization' }] : []\n}\n\n/* ------------------------------------------------------------------------ *\n * The site logo\n * ------------------------------------------------------------------------ */\n\nexport type AiThemeLogoLocation = AiMediaAssetLocation\n\n/**\n * Where a site's logo lives in a media library: a media reference or a CDN\n * path naming this site's library, or its org's. Anything else — an external\n * URL, an asset of some other site or org — has no location, and the logo is\n * left alone. The media library's own rule, shared with every job that reads\n * an asset (`ai-media-asset.ts`).\n */\nexport const logoMediaLocation = aiMediaAssetLocation\n\n/**\n * The dominant colors of an image's pixels: transparent pixels skipped, the\n * rest grouped into coarse buckets, the busiest distinct buckets first, and\n * the near-white and near-black edges a logo sits on dropped while anything\n * else is there.\n */\nexport function dominantColorsFromPixels(\n data: ArrayLike<number>,\n channels: number,\n count = AI_THEME_BRAND_COLORS_PER_SOURCE,\n): string[] {\n const buckets = new Map<number, { sum: Rgb; pixels: number }>()\n for (let index = 0; index + channels <= data.length; index += channels) {\n if (channels >= 4 && data[index + 3] < 128) continue\n const pixel: Rgb = [data[index], data[index + 1], data[index + 2]]\n const key = ((pixel[0] >> 4) << 8) | ((pixel[1] >> 4) << 4) | (pixel[2] >> 4)\n const bucket = buckets.get(key) ?? { sum: [0, 0, 0] as Rgb, pixels: 0 }\n bucket.sum = [bucket.sum[0] + pixel[0], bucket.sum[1] + pixel[1], bucket.sum[2] + pixel[2]]\n bucket.pixels += 1\n buckets.set(key, bucket)\n }\n const ranked = [...buckets.values()]\n .sort((a, b) => b.pixels - a.pixels)\n .map(({ sum, pixels }): Rgb => [sum[0] / pixels, sum[1] / pixels, sum[2] / pixels])\n const edge = (color: Rgb) => Math.min(...color) > 235 || Math.max(...color) < 20\n const inside = ranked.filter((color) => !edge(color))\n return distinctColors(inside.length ? inside : ranked, count)\n}\n\n/** The part of `sharp` the logo read uses. */\ninterface SharpPixelPipeline {\n resize(width: number, height: number, options: { fit: 'inside' }): SharpPixelPipeline\n ensureAlpha(): SharpPixelPipeline\n raw(): SharpPixelPipeline\n toBuffer(options: { resolveWithObject: true }): Promise<{\n data: Buffer\n info: { channels: number }\n }>\n}\ntype SharpPixels = (input: Buffer, options?: { limitInputPixels?: number }) => SharpPixelPipeline\n\nconst LOGO_TYPES = /^image\\/(png|jpeg|webp|gif|avif|svg\\+xml)$/\n\n/**\n * A library logo's colors, read the way the media CDN would serve it\n * (`readAiMediaAssetBytes`). `null` when the asset may not be read or the bytes\n * cannot be decoded.\n */\nasync function readLogoColors(\n firestore: FirebaseFirestore.Firestore,\n location: AiThemeLogoLocation,\n hostId: string,\n): Promise<string[] | null> {\n const asset = await readAiMediaAssetBytes(firestore, location, hostId, {\n maxBytes: AI_THEME_LOGO_MAX_BYTES,\n types: LOGO_TYPES,\n })\n if (!asset) return null\n const sharp = (await loadAiMediaSharp()) as SharpPixels\n const { data, info } = await sharp(asset.buffer, { limitInputPixels: 64_000_000 })\n .resize(48, 48, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true })\n return dominantColorsFromPixels(data, info.channels)\n}\n\n/* ------------------------------------------------------------------------ *\n * A page the brief links to\n * ------------------------------------------------------------------------ */\n\nconst URL_IN_BRIEF = /\\bhttps?:\\/\\/[^\\s<>\"'`)\\]]+/i\n\n/** The first https link in a brief, or `null` — an http link is not followed. */\nexport function referenceUrlOf(brief: string): URL | null {\n const match = URL_IN_BRIEF.exec(brief)\n if (!match) return null\n try {\n const url = new URL(match[0].replace(/[.,;:!?]+$/, ''))\n return url.protocol === 'https:' ? url : null\n } catch {\n return null\n }\n}\n\n/** How much a page's declared `theme-color` counts beside one use of a color. */\nexport const AI_THEME_THEME_COLOR_WEIGHT = 25\n\nconst THEME_COLOR_META = /<meta\\b[^>]*\\bname\\s*=\\s*[\"']?theme-color\\b[^>]*>/gi\nconst CONTENT_ATTRIBUTE = /\\bcontent\\s*=\\s*[\"']?\\s*([^\"'\\s>]+)/i\n// A `#` after `&` is a character reference (`'`), not a color.\nconst HEX_LITERAL = /(?<!&)#(?:[0-9a-f]{6}|[0-9a-f]{3})(?![0-9a-z])/gi\nconst RGB_LITERAL = /rgba?\\(\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})\\s*[,\\s]\\s*(\\d{1,3})/gi\n\n/** Every color a page's markup or a stylesheet uses, weighted by how often. */\nexport function colorsFromMarkup(text: string): Map<string, number> {\n const weights = new Map<string, number>()\n const add = (hex: string | null, weight: number) => {\n if (hex) weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n for (const tag of text.match(THEME_COLOR_META) ?? []) {\n add(normalizeThemeHex(CONTENT_ATTRIBUTE.exec(tag)?.[1] ?? ''), AI_THEME_THEME_COLOR_WEIGHT)\n }\n for (const match of text.matchAll(HEX_LITERAL)) add(normalizeThemeHex(match[0]), 1)\n for (const match of text.matchAll(RGB_LITERAL)) {\n add(toHex([Number(match[1]), Number(match[2]), Number(match[3])]), 1)\n }\n return weights\n}\n\nconst STYLESHEET_LINK = /<link\\b[^>]*\\brel\\s*=\\s*[\"']?stylesheet\\b[^>]*>/gi\nconst HREF_ATTRIBUTE = /\\bhref\\s*=\\s*[\"']?\\s*([^\"'\\s>]+)/i\n\n/** The https stylesheets a page links, resolved against it, at most a few. */\nexport function stylesheetUrlsOf(html: string, pageUrl: string): string[] {\n const urls: string[] = []\n for (const tag of html.match(STYLESHEET_LINK) ?? []) {\n const href = HREF_ATTRIBUTE.exec(tag)?.[1]\n if (!href) continue\n try {\n const url = new URL(href, pageUrl)\n if (url.protocol === 'https:' && !urls.includes(url.toString())) urls.push(url.toString())\n } catch {\n continue\n }\n if (urls.length === AI_THEME_REFERENCE_STYLESHEETS) break\n }\n return urls\n}\n\n/** The most used distinct colors, the colorful before the greys. */\nexport function rankBrandColors(\n weights: ReadonlyMap<string, number>,\n count = AI_THEME_BRAND_COLORS_PER_SOURCE,\n): string[] {\n const ranked = [...weights.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([hex]) => channelsOf(hex))\n return distinctColors(ranked, count)\n}\n\n/** One abort for the caller's signal and a clock, whichever ends first. */\nfunction deadline(signal: AbortSignal | undefined, ms: number) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), ms)\n const onAbort = () => controller.abort()\n if (signal?.aborted) controller.abort()\n signal?.addEventListener('abort', onAbort)\n return {\n signal: controller.signal,\n done: () => {\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n },\n }\n}\n\nasync function readCapped(body: ReadableStream<Uint8Array>, maxBytes: number): Promise<string> {\n const reader = body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done || !value) break\n const room = maxBytes - total\n if (value.byteLength >= room) {\n chunks.push(value.subarray(0, room))\n await reader.cancel().catch(() => undefined)\n break\n }\n chunks.push(value)\n total += value.byteLength\n }\n const joined = new Uint8Array(chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0))\n let offset = 0\n for (const chunk of chunks) {\n joined.set(chunk, offset)\n offset += chunk.byteLength\n }\n return new TextDecoder().decode(joined)\n}\n\n/**\n * A public https resource as text, through the plugin fetch proxy's SSRF\n * guard: the host must resolve only to public addresses and the socket is\n * pinned to the address checked; no credentials in the URL and no port but\n * the default; every redirect re-enters the same checks; the body is read to\n * a byte cap under a clock, and only a content type the caller accepts is\n * read at all. `null` for anything that does not hold.\n */\nexport async function fetchPublicText(\n url: string,\n options: { maxBytes: number; accept: RegExp; signal?: AbortSignal; timeoutMs?: number },\n): Promise<{ text: string; url: string } | null> {\n const { createPinnedDispatcher, resolvePublicIp } = await import('./ai-theme-brand-server')\n let current = url\n for (let hop = 0; hop <= MAX_REDIRECT_HOPS; hop += 1) {\n let parsed: URL\n try {\n parsed = new URL(current)\n } catch {\n return null\n }\n if (parsed.protocol !== 'https:' || parsed.username || parsed.password) return null\n if (parsed.port && parsed.port !== '443') return null\n const pinned = await resolvePublicIp(parsed.hostname)\n if (!pinned) return null\n const dispatcher = createPinnedDispatcher(pinned)\n const clock = deadline(options.signal, options.timeoutMs ?? FETCH_TIMEOUT_MS)\n try {\n // `dispatcher` is undici's, which the global fetch honors; a variable\n // rather than a literal keeps the DOM `RequestInit` check off it.\n const init = {\n method: 'GET',\n redirect: 'manual' as const,\n signal: clock.signal,\n dispatcher,\n headers: { Accept: 'text/html, text/css;q=0.9' },\n }\n const response = await fetch(parsed.toString(), init as RequestInit)\n if (REDIRECT_STATUSES.has(response.status)) {\n const location = response.headers.get('location')\n if (!location) return null\n current = new URL(location, parsed).toString()\n continue\n }\n if (!response.ok || !response.body) return null\n if (!options.accept.test(response.headers.get('content-type') ?? '')) return null\n return { text: await readCapped(response.body, options.maxBytes), url: parsed.toString() }\n } catch {\n return null\n } finally {\n clock.done()\n await dispatcher.close().catch(() => undefined)\n }\n }\n return null\n}\n\n/**\n * A public page as it was read: its markup and the text of its first\n * stylesheets ({@link AI_THEME_REFERENCE_STYLESHEETS}), each under its byte\n * cap. What a theme job takes its colors from and a site start its\n * inspiration signals (AGL-3975); never a crawl, one page and its sheets.\n */\nexport interface AiReferencePage {\n /** The address the markup was finally read at, after redirects. */\n url: string\n html: string\n stylesheets: string[]\n}\n\n/** A page and its first stylesheets through {@link fetchPublicText}; `null` when the page cannot be read. */\nexport async function readReferencePage(url: URL, signal: AbortSignal): Promise<AiReferencePage | null> {\n const page = await fetchPublicText(url.toString(), {\n maxBytes: AI_THEME_REFERENCE_MAX_BYTES,\n accept: /text\\/html|application\\/xhtml\\+xml/i,\n signal,\n })\n if (!page) return null\n const stylesheets: string[] = []\n for (const sheet of stylesheetUrlsOf(page.text, page.url)) {\n const css = await fetchPublicText(sheet, {\n maxBytes: AI_THEME_STYLESHEET_MAX_BYTES,\n accept: /text\\/css/i,\n signal,\n })\n if (css) stylesheets.push(css.text)\n }\n return { url: page.url, html: page.text, stylesheets }\n}\n\n/** A read page's brand colors: every color its markup and stylesheets use, ranked. */\nexport function referencePageColors(page: Pick<AiReferencePage, 'html' | 'stylesheets'>): string[] {\n const weights = colorsFromMarkup(page.html)\n for (const css of page.stylesheets) {\n for (const [hex, weight] of colorsFromMarkup(css)) {\n weights.set(hex, (weights.get(hex) ?? 0) + weight)\n }\n }\n return rankBrandColors(weights)\n}\n\n/** A linked page's colors, from its markup and its first stylesheets. */\nasync function readReferenceColors(url: URL, signal: AbortSignal): Promise<string[] | null> {\n const page = await readReferencePage(url, signal)\n return page ? referencePageColors(page) : null\n}\n\n/* ------------------------------------------------------------------------ *\n * Gathering\n * ------------------------------------------------------------------------ */\n\n/** The two reads that leave the process, replaceable where a spec needs to. */\nexport interface AiThemeBrandSeams {\n logoColors?: (\n firestore: FirebaseFirestore.Firestore,\n location: AiThemeLogoLocation,\n hostId: string,\n ) => Promise<string[] | null>\n referenceColors?: (url: URL, signal: AbortSignal) => Promise<string[] | null>\n}\n\nexport async function gatherAiThemeBrandInputs(\n input: {\n firestore: FirebaseFirestore.Firestore\n org: Partial<AglynOrgBilling> | null\n hostId: string\n /** The site's host document. */\n host: Record<string, unknown>\n brief: string\n signal?: AbortSignal\n },\n seams: AiThemeBrandSeams = {},\n): Promise<AiThemeBrandInputs> {\n const colors: AiThemeBrandColor[] = [...organizationBrandColors(input.org)]\n const notes: string[] = []\n const clock = deadline(input.signal, AI_THEME_BRAND_BUDGET_MS)\n const add = (found: readonly string[], source: AiThemeBrandSource) => {\n for (const hex of found.slice(0, AI_THEME_BRAND_COLORS_PER_SOURCE)) {\n if (!colors.some((color) => color.hex === hex)) colors.push({ hex, source })\n }\n }\n try {\n const location = logoMediaLocation(\n input.host['logoUrl'],\n input.hostId,\n String(input.host['orgId'] ?? ''),\n )\n if (location) {\n const found = await (seams.logoColors ?? readLogoColors)(\n input.firestore,\n location,\n input.hostId,\n ).catch(() => null)\n if (found) add(found, 'logo')\n else notes.push(\"The site logo's colors could not be read, so the proposal does not use them.\")\n }\n const reference = referenceUrlOf(input.brief)\n if (reference) {\n const found = clock.signal.aborted\n ? null\n : await (seams.referenceColors ?? readReferenceColors)(reference, clock.signal).catch(\n () => null,\n )\n if (found?.length) add(found, 'reference')\n else {\n notes.push(\n `The page at ${reference.hostname} could not be read for its colors, so the proposal does not use them.`,\n )\n }\n }\n } finally {\n clock.done()\n }\n return { colors, notes }\n}\n"],"names":["checkEntitlement","normalizeThemeHex","AI_THEME_BRAND_BUDGET_MS","aiMediaAssetLocation","loadAiMediaSharp","readAiMediaAssetBytes","AI_THEME_BRAND_SOURCE_WORDS","organization","logo","reference","AI_THEME_BRAND_COLORS_PER_SOURCE","AI_THEME_LOGO_MAX_BYTES","AI_THEME_REFERENCE_MAX_BYTES","AI_THEME_STYLESHEET_MAX_BYTES","AI_THEME_REFERENCE_STYLESHEETS","FETCH_TIMEOUT_MS","MAX_REDIRECT_HOPS","REDIRECT_STATUSES","Set","toHex","red","green","blue","map","channel","Math","round","min","max","toString","padStart","join","channelsOf","hex","parseInt","slice","distance","a","b","hypot","chroma","SAME_COLOR_DISTANCE","GREY_CHROMA","distinctColors","ranked","count","colorful","filter","color","chosen","length","some","other","push","organizationBrandColors","org","raw","brandingProfile","primaryColor","source","logoMediaLocation","dominantColorsFromPixels","data","channels","buckets","Map","index","pixel","key","bucket","get","sum","pixels","set","values","sort","edge","inside","LOGO_TYPES","readLogoColors","firestore","location","hostId","asset","maxBytes","types","sharp","info","buffer","limitInputPixels","resize","fit","ensureAlpha","toBuffer","resolveWithObject","URL_IN_BRIEF","referenceUrlOf","brief","match","exec","url","URL","replace","protocol","AI_THEME_THEME_COLOR_WEIGHT","THEME_COLOR_META","CONTENT_ATTRIBUTE","HEX_LITERAL","RGB_LITERAL","colorsFromMarkup","text","weights","add","weight","tag","matchAll","Number","STYLESHEET_LINK","HREF_ATTRIBUTE","stylesheetUrlsOf","html","pageUrl","urls","href","includes","rankBrandColors","entries","deadline","signal","ms","controller","AbortController","timer","setTimeout","abort","onAbort","aborted","addEventListener","done","clearTimeout","removeEventListener","readCapped","body","reader","getReader","chunks","total","value","read","room","byteLength","subarray","cancel","catch","undefined","joined","Uint8Array","reduce","chunk","offset","TextDecoder","decode","fetchPublicText","options","createPinnedDispatcher","resolvePublicIp","current","hop","parsed","username","password","port","pinned","hostname","dispatcher","clock","timeoutMs","response","init","method","redirect","headers","Accept","fetch","has","status","ok","accept","test","close","readReferencePage","page","stylesheets","sheet","css","referencePageColors","readReferenceColors","gatherAiThemeBrandInputs","input","seams","colors","notes","found","host","String","logoColors","referenceColors"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,2CAA0C;AAK3E,SAASC,iBAAiB,QAAQ,4BAAwB;AAC1D,SAASC,wBAAwB,QAAQ,2BAAuB;AAChE,SACEC,oBAAoB,EACpBC,gBAAgB,EAChBC,qBAAqB,QAEhB,sBAAkB;AA0BzB,2DAA2D,GAC3D,OAAO,MAAMC,8BAAkE;IAC7EC,cAAc;IACdC,MAAM;IACNC,WAAW;AACb,EAAC;AAaD,4CAA4C,GAC5C,OAAO,MAAMC,mCAAmC,EAAC;AAEjD,6CAA6C,GAC7C,OAAO,MAAMC,0BAA0B,IAAI,OAAO,KAAI;AAEtD,6EAA6E,GAC7E,OAAO,MAAMC,+BAA+B,MAAM,KAAI;AACtD,OAAO,MAAMC,gCAAgC,MAAM,KAAI;AAEvD,8DAA8D,GAC9D,OAAO,MAAMC,iCAAiC,EAAC;AAE/C;;;CAGC,GACD,SAASZ,wBAAwB,GAAE;AAEnC,MAAMa,mBAAmB;AACzB,MAAMC,oBAAoB;AAC1B,MAAMC,oBAAoB,IAAIC,IAAI;IAAC;IAAK;IAAK;IAAK;IAAK;CAAI;AAI3D,MAAMC,QAAQ,CAAC,CAACC,KAAKC,OAAOC,KAAU,GACpC,CAAC,CAAC,EAAE;QAACF;QAAKC;QAAOC;KAAK,CACnBC,GAAG,CAAC,CAACC,UAAYC,KAAKC,KAAK,CAACD,KAAKE,GAAG,CAAC,KAAKF,KAAKG,GAAG,CAAC,GAAGJ,WAAWK,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAC1FC,IAAI,CAAC,KAAK;AAEf,MAAMC,aAAa,CAACC,MAAqB;QACvCC,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;QAC1BD,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;QAC1BD,SAASD,IAAIE,KAAK,CAAC,GAAG,IAAI;KAC3B;AAED,MAAMC,WAAW,CAACC,GAAQC,IAAWb,KAAKc,KAAK,CAACF,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE,EAAED,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE,EAAED,CAAC,CAAC,EAAE,GAAGC,CAAC,CAAC,EAAE;AAErF,8DAA8D,GAC9D,MAAME,SAAS,CAAC,CAACpB,KAAKC,OAAOC,KAAU,GAAKG,KAAKG,GAAG,CAACR,KAAKC,OAAOC,QAAQG,KAAKE,GAAG,CAACP,KAAKC,OAAOC;AAE9F,iEAAiE,GACjE,MAAMmB,sBAAsB;AAE5B,0EAA0E,GAC1E,MAAMC,cAAc;AAEpB,kFAAkF,GAClF,SAASC,eAAeC,MAAsB,EAAEC,KAAa;IAC3D,MAAMC,WAAWF,OAAOG,MAAM,CAAC,CAACC,QAAUR,OAAOQ,SAASN;IAC1D,MAAMO,SAAgB,EAAE;IACxB,KAAK,MAAMD,SAASF,SAASI,MAAM,GAAGJ,WAAWF,OAAQ;QACvD,IAAIK,OAAOE,IAAI,CAAC,CAACC,QAAUhB,SAASgB,OAAOJ,SAASP,sBAAsB;QAC1EQ,OAAOI,IAAI,CAACL;QACZ,IAAIC,OAAOC,MAAM,KAAKL,OAAO;IAC/B;IACA,OAAOI,OAAO1B,GAAG,CAACJ;AACpB;AAEA;;4EAE4E,GAE5E,gFAAgF,GAChF,OAAO,SAASmC,wBACdC,GAAgD;QAGnCA;IADb,IAAI,CAACvD,iBAAiBuD,KAAK,eAAe,OAAO,EAAE;IACnD,MAAMC,MAAOD,wBAAAA,uBAAAA,IAAKE,eAAe,qBAArB,AAACF,qBAAyDG,YAAY;IAClF,MAAMzB,MAAM,OAAOuB,QAAQ,WAAWvD,kBAAkBuD,OAAO;IAC/D,OAAOvB,MAAM;QAAC;YAAEA;YAAK0B,QAAQ;QAAe;KAAE,GAAG,EAAE;AACrD;AAQA;;;;;;CAMC,GACD,OAAO,MAAMC,oBAAoBzD,qBAAoB;AAErD;;;;;CAKC,GACD,OAAO,SAAS0D,yBACdC,IAAuB,EACvBC,QAAgB,EAChBlB,QAAQnC,gCAAgC;IAExC,MAAMsD,UAAU,IAAIC;IACpB,IAAK,IAAIC,QAAQ,GAAGA,QAAQH,YAAYD,KAAKZ,MAAM,EAAEgB,SAASH,SAAU;YAIvDC;QAHf,IAAID,YAAY,KAAKD,IAAI,CAACI,QAAQ,EAAE,GAAG,KAAK;QAC5C,MAAMC,QAAa;YAACL,IAAI,CAACI,MAAM;YAAEJ,IAAI,CAACI,QAAQ,EAAE;YAAEJ,IAAI,CAACI,QAAQ,EAAE;SAAC;QAClE,MAAME,MAAM,AAAED,KAAK,CAAC,EAAE,IAAI,KAAM,IAAM,AAACA,KAAK,CAAC,EAAE,IAAI,KAAM,IAAMA,KAAK,CAAC,EAAE,IAAI;QAC3E,MAAME,UAASL,eAAAA,QAAQM,GAAG,CAACF,gBAAZJ,eAAoB;YAAEO,KAAK;gBAAC;gBAAG;gBAAG;aAAE;YAASC,QAAQ;QAAE;QACtEH,OAAOE,GAAG,GAAG;YAACF,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;YAAEE,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;YAAEE,OAAOE,GAAG,CAAC,EAAE,GAAGJ,KAAK,CAAC,EAAE;SAAC;QAC3FE,OAAOG,MAAM,IAAI;QACjBR,QAAQS,GAAG,CAACL,KAAKC;IACnB;IACA,MAAMzB,SAAS;WAAIoB,QAAQU,MAAM;KAAG,CACjCC,IAAI,CAAC,CAACtC,GAAGC,IAAMA,EAAEkC,MAAM,GAAGnC,EAAEmC,MAAM,EAClCjD,GAAG,CAAC,CAAC,EAAEgD,GAAG,EAAEC,MAAM,EAAE,GAAU;YAACD,GAAG,CAAC,EAAE,GAAGC;YAAQD,GAAG,CAAC,EAAE,GAAGC;YAAQD,GAAG,CAAC,EAAE,GAAGC;SAAO;IACpF,MAAMI,OAAO,CAAC5B,QAAevB,KAAKE,GAAG,IAAIqB,SAAS,OAAOvB,KAAKG,GAAG,IAAIoB,SAAS;IAC9E,MAAM6B,SAASjC,OAAOG,MAAM,CAAC,CAACC,QAAU,CAAC4B,KAAK5B;IAC9C,OAAOL,eAAekC,OAAO3B,MAAM,GAAG2B,SAASjC,QAAQC;AACzD;AAcA,MAAMiC,aAAa;AAEnB;;;;CAIC,GACD,eAAeC,eACbC,SAAsC,EACtCC,QAA6B,EAC7BC,MAAc;IAEd,MAAMC,QAAQ,MAAM9E,sBAAsB2E,WAAWC,UAAUC,QAAQ;QACrEE,UAAUzE;QACV0E,OAAOP;IACT;IACA,IAAI,CAACK,OAAO,OAAO;IACnB,MAAMG,QAAS,MAAMlF;IACrB,MAAM,EAAE0D,IAAI,EAAEyB,IAAI,EAAE,GAAG,MAAMD,MAAMH,MAAMK,MAAM,EAAE;QAAEC,kBAAkB;IAAW,GAC7EC,MAAM,CAAC,IAAI,IAAI;QAAEC,KAAK;IAAS,GAC/BC,WAAW,GACXpC,GAAG,GACHqC,QAAQ,CAAC;QAAEC,mBAAmB;IAAK;IACtC,OAAOjC,yBAAyBC,MAAMyB,KAAKxB,QAAQ;AACrD;AAEA;;4EAE4E,GAE5E,MAAMgC,eAAe;AAErB,+EAA+E,GAC/E,OAAO,SAASC,eAAeC,KAAa;IAC1C,MAAMC,QAAQH,aAAaI,IAAI,CAACF;IAChC,IAAI,CAACC,OAAO,OAAO;IACnB,IAAI;QACF,MAAME,MAAM,IAAIC,IAAIH,KAAK,CAAC,EAAE,CAACI,OAAO,CAAC,cAAc;QACnD,OAAOF,IAAIG,QAAQ,KAAK,WAAWH,MAAM;IAC3C,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA,+EAA+E,GAC/E,OAAO,MAAMI,8BAA8B,GAAE;AAE7C,MAAMC,mBAAmB;AACzB,MAAMC,oBAAoB;AAC1B,oEAAoE;AACpE,MAAMC,cAAc;AACpB,MAAMC,cAAc;AAEpB,6EAA6E,GAC7E,OAAO,SAASC,iBAAiBC,IAAY;QAKzBA;IAJlB,MAAMC,UAAU,IAAI9C;IACpB,MAAM+C,MAAM,CAAC/E,KAAoBgF;YACJF;QAA3B,IAAI9E,KAAK8E,QAAQtC,GAAG,CAACxC,KAAK,EAAC8E,eAAAA,QAAQzC,GAAG,CAACrC,gBAAZ8E,eAAoB,KAAKE;IACtD;IACA,KAAK,MAAMC,QAAOJ,cAAAA,KAAKZ,KAAK,CAACO,6BAAXK,cAAgC,EAAE,CAAE;;YAC9BJ;QAAtBM,IAAI/G,2BAAkByG,0BAAAA,kBAAkBP,IAAI,CAACe,yBAAvBR,uBAA6B,CAAC,EAAE,mBAAI,KAAKF;IACjE;IACA,KAAK,MAAMN,SAASY,KAAKK,QAAQ,CAACR,aAAcK,IAAI/G,kBAAkBiG,KAAK,CAAC,EAAE,GAAG;IACjF,KAAK,MAAMA,SAASY,KAAKK,QAAQ,CAACP,aAAc;QAC9CI,IAAI7F,MAAM;YAACiG,OAAOlB,KAAK,CAAC,EAAE;YAAGkB,OAAOlB,KAAK,CAAC,EAAE;YAAGkB,OAAOlB,KAAK,CAAC,EAAE;SAAE,GAAG;IACrE;IACA,OAAOa;AACT;AAEA,MAAMM,kBAAkB;AACxB,MAAMC,iBAAiB;AAEvB,4EAA4E,GAC5E,OAAO,SAASC,iBAAiBC,IAAY,EAAEC,OAAe;QAE1CD;IADlB,MAAME,OAAiB,EAAE;IACzB,KAAK,MAAMR,QAAOM,cAAAA,KAAKtB,KAAK,CAACmB,4BAAXG,cAA+B,EAAE,CAAE;YACtCF;QAAb,MAAMK,QAAOL,uBAAAA,eAAenB,IAAI,CAACe,yBAApBI,oBAA0B,CAAC,EAAE;QAC1C,IAAI,CAACK,MAAM;QACX,IAAI;YACF,MAAMvB,MAAM,IAAIC,IAAIsB,MAAMF;YAC1B,IAAIrB,IAAIG,QAAQ,KAAK,YAAY,CAACmB,KAAKE,QAAQ,CAACxB,IAAIvE,QAAQ,KAAK6F,KAAKrE,IAAI,CAAC+C,IAAIvE,QAAQ;QACzF,EAAE,eAAM;YACN;QACF;QACA,IAAI6F,KAAKxE,MAAM,KAAKpC,gCAAgC;IACtD;IACA,OAAO4G;AACT;AAEA,kEAAkE,GAClE,OAAO,SAASG,gBACdd,OAAoC,EACpClE,QAAQnC,gCAAgC;IAExC,MAAMkC,SAAS;WAAImE,QAAQe,OAAO;KAAG,CAClCnD,IAAI,CAAC,CAACtC,GAAGC,IAAMA,CAAC,CAAC,EAAE,GAAGD,CAAC,CAAC,EAAE,EAC1Bd,GAAG,CAAC,CAAC,CAACU,IAAI,GAAKD,WAAWC;IAC7B,OAAOU,eAAeC,QAAQC;AAChC;AAEA,yEAAyE,GACzE,SAASkF,SAASC,MAA+B,EAAEC,EAAU;IAC3D,MAAMC,aAAa,IAAIC;IACvB,MAAMC,QAAQC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IACnD,MAAMM,UAAU,IAAML,WAAWI,KAAK;IACtC,IAAIN,0BAAAA,OAAQQ,OAAO,EAAEN,WAAWI,KAAK;IACrCN,0BAAAA,OAAQS,gBAAgB,CAAC,SAASF;IAClC,OAAO;QACLP,QAAQE,WAAWF,MAAM;QACzBU,MAAM;YACJC,aAAaP;YACbJ,0BAAAA,OAAQY,mBAAmB,CAAC,SAASL;QACvC;IACF;AACF;AAEA,eAAeM,WAAWC,IAAgC,EAAE1D,QAAgB;IAC1E,MAAM2D,SAASD,KAAKE,SAAS;IAC7B,MAAMC,SAAuB,EAAE;IAC/B,IAAIC,QAAQ;IACZ,OAAS;QACP,MAAM,EAAER,IAAI,EAAES,KAAK,EAAE,GAAG,MAAMJ,OAAOK,IAAI;QACzC,IAAIV,QAAQ,CAACS,OAAO;QACpB,MAAME,OAAOjE,WAAW8D;QACxB,IAAIC,MAAMG,UAAU,IAAID,MAAM;YAC5BJ,OAAO5F,IAAI,CAAC8F,MAAMI,QAAQ,CAAC,GAAGF;YAC9B,MAAMN,OAAOS,MAAM,GAAGC,KAAK,CAAC,IAAMC;YAClC;QACF;QACAT,OAAO5F,IAAI,CAAC8F;QACZD,SAASC,MAAMG,UAAU;IAC3B;IACA,MAAMK,SAAS,IAAIC,WAAWX,OAAOY,MAAM,CAAC,CAACtF,KAAKuF,QAAUvF,MAAMuF,MAAMR,UAAU,EAAE;IACpF,IAAIS,SAAS;IACb,KAAK,MAAMD,SAASb,OAAQ;QAC1BU,OAAOlF,GAAG,CAACqF,OAAOC;QAClBA,UAAUD,MAAMR,UAAU;IAC5B;IACA,OAAO,IAAIU,cAAcC,MAAM,CAACN;AAClC;AAEA;;;;;;;CAOC,GACD,OAAO,eAAeO,gBACpB9D,GAAW,EACX+D,OAAuF;IAEvF,MAAM,EAAEC,sBAAsB,EAAEC,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;IACjE,IAAIC,UAAUlE;IACd,IAAK,IAAImE,MAAM,GAAGA,OAAOvJ,mBAAmBuJ,OAAO,EAAG;YAYbJ;QAXvC,IAAIK;QACJ,IAAI;YACFA,SAAS,IAAInE,IAAIiE;QACnB,EAAE,eAAM;YACN,OAAO;QACT;QACA,IAAIE,OAAOjE,QAAQ,KAAK,YAAYiE,OAAOC,QAAQ,IAAID,OAAOE,QAAQ,EAAE,OAAO;QAC/E,IAAIF,OAAOG,IAAI,IAAIH,OAAOG,IAAI,KAAK,OAAO,OAAO;QACjD,MAAMC,SAAS,MAAMP,gBAAgBG,OAAOK,QAAQ;QACpD,IAAI,CAACD,QAAQ,OAAO;QACpB,MAAME,aAAaV,uBAAuBQ;QAC1C,MAAMG,QAAQhD,SAASoC,QAAQnC,MAAM,GAAEmC,qBAAAA,QAAQa,SAAS,YAAjBb,qBAAqBpJ;QAC5D,IAAI;gBAkBuBkK;YAjBzB,sEAAsE;YACtE,kEAAkE;YAClE,MAAMC,OAAO;gBACXC,QAAQ;gBACRC,UAAU;gBACVpD,QAAQ+C,MAAM/C,MAAM;gBACpB8C;gBACAO,SAAS;oBAAEC,QAAQ;gBAA4B;YACjD;YACA,MAAML,WAAW,MAAMM,MAAMf,OAAO3I,QAAQ,IAAIqJ;YAChD,IAAIjK,kBAAkBuK,GAAG,CAACP,SAASQ,MAAM,GAAG;gBAC1C,MAAMxG,WAAWgG,SAASI,OAAO,CAAC/G,GAAG,CAAC;gBACtC,IAAI,CAACW,UAAU,OAAO;gBACtBqF,UAAU,IAAIjE,IAAIpB,UAAUuF,QAAQ3I,QAAQ;gBAC5C;YACF;YACA,IAAI,CAACoJ,SAASS,EAAE,IAAI,CAACT,SAASnC,IAAI,EAAE,OAAO;YAC3C,IAAI,CAACqB,QAAQwB,MAAM,CAACC,IAAI,EAACX,wBAAAA,SAASI,OAAO,CAAC/G,GAAG,CAAC,2BAArB2G,wBAAwC,KAAK,OAAO;YAC7E,OAAO;gBAAEnE,MAAM,MAAM+B,WAAWoC,SAASnC,IAAI,EAAEqB,QAAQ/E,QAAQ;gBAAGgB,KAAKoE,OAAO3I,QAAQ;YAAG;QAC3F,EAAE,eAAM;YACN,OAAO;QACT,SAAU;YACRkJ,MAAMrC,IAAI;YACV,MAAMoC,WAAWe,KAAK,GAAGpC,KAAK,CAAC,IAAMC;QACvC;IACF;IACA,OAAO;AACT;AAeA,2GAA2G,GAC3G,OAAO,eAAeoC,kBAAkB1F,GAAQ,EAAE4B,MAAmB;IACnE,MAAM+D,OAAO,MAAM7B,gBAAgB9D,IAAIvE,QAAQ,IAAI;QACjDuD,UAAUxE;QACV+K,QAAQ;QACR3D;IACF;IACA,IAAI,CAAC+D,MAAM,OAAO;IAClB,MAAMC,cAAwB,EAAE;IAChC,KAAK,MAAMC,SAAS1E,iBAAiBwE,KAAKjF,IAAI,EAAEiF,KAAK3F,GAAG,EAAG;QACzD,MAAM8F,MAAM,MAAMhC,gBAAgB+B,OAAO;YACvC7G,UAAUvE;YACV8K,QAAQ;YACR3D;QACF;QACA,IAAIkE,KAAKF,YAAY3I,IAAI,CAAC6I,IAAIpF,IAAI;IACpC;IACA,OAAO;QAAEV,KAAK2F,KAAK3F,GAAG;QAAEoB,MAAMuE,KAAKjF,IAAI;QAAEkF;IAAY;AACvD;AAEA,oFAAoF,GACpF,OAAO,SAASG,oBAAoBJ,IAAmD;IACrF,MAAMhF,UAAUF,iBAAiBkF,KAAKvE,IAAI;IAC1C,KAAK,MAAM0E,OAAOH,KAAKC,WAAW,CAAE;QAClC,KAAK,MAAM,CAAC/J,KAAKgF,OAAO,IAAIJ,iBAAiBqF,KAAM;gBAC/BnF;YAAlBA,QAAQtC,GAAG,CAACxC,KAAK,EAAC8E,eAAAA,QAAQzC,GAAG,CAACrC,gBAAZ8E,eAAoB,KAAKE;QAC7C;IACF;IACA,OAAOY,gBAAgBd;AACzB;AAEA,uEAAuE,GACvE,eAAeqF,oBAAoBhG,GAAQ,EAAE4B,MAAmB;IAC9D,MAAM+D,OAAO,MAAMD,kBAAkB1F,KAAK4B;IAC1C,OAAO+D,OAAOI,oBAAoBJ,QAAQ;AAC5C;AAgBA,OAAO,eAAeM,yBACpBC,KAQC,EACDC,QAA2B,CAAC,CAAC;IAE7B,MAAMC,SAA8B;WAAIlJ,wBAAwBgJ,MAAM/I,GAAG;KAAE;IAC3E,MAAMkJ,QAAkB,EAAE;IAC1B,MAAM1B,QAAQhD,SAASuE,MAAMtE,MAAM,EAAE9H;IACrC,MAAM8G,MAAM,CAAC0F,OAA0B/I;QACrC,KAAK,MAAM1B,OAAOyK,MAAMvK,KAAK,CAAC,GAAGzB,kCAAmC;YAClE,IAAI,CAAC8L,OAAOrJ,IAAI,CAAC,CAACH,QAAUA,MAAMf,GAAG,KAAKA,MAAMuK,OAAOnJ,IAAI,CAAC;gBAAEpB;gBAAK0B;YAAO;QAC5E;IACF;IACA,IAAI;YAIO2I;QAHT,MAAMrH,WAAWrB,kBACf0I,MAAMK,IAAI,CAAC,UAAU,EACrBL,MAAMpH,MAAM,EACZ0H,QAAON,oBAAAA,MAAMK,IAAI,CAAC,QAAQ,YAAnBL,oBAAuB;QAEhC,IAAIrH,UAAU;gBACSsH;YAArB,MAAMG,QAAQ,MAAM,EAACH,oBAAAA,MAAMM,UAAU,YAAhBN,oBAAoBxH,gBACvCuH,MAAMtH,SAAS,EACfC,UACAqH,MAAMpH,MAAM,EACZuE,KAAK,CAAC,IAAM;YACd,IAAIiD,OAAO1F,IAAI0F,OAAO;iBACjBD,MAAMpJ,IAAI,CAAC;QAClB;QACA,MAAM5C,YAAYuF,eAAesG,MAAMrG,KAAK;QAC5C,IAAIxF,WAAW;gBAGF8L;YAFX,MAAMG,QAAQ3B,MAAM/C,MAAM,CAACQ,OAAO,GAC9B,OACA,MAAM,EAAC+D,yBAAAA,MAAMO,eAAe,YAArBP,yBAAyBH,qBAAqB3L,WAAWsK,MAAM/C,MAAM,EAAEyB,KAAK,CACjF,IAAM;YAEZ,IAAIiD,yBAAAA,MAAOxJ,MAAM,EAAE8D,IAAI0F,OAAO;iBACzB;gBACHD,MAAMpJ,IAAI,CACR,CAAC,YAAY,EAAE5C,UAAUoK,QAAQ,CAAC,qEAAqE,CAAC;YAE5G;QACF;IACF,SAAU;QACRE,MAAMrC,IAAI;IACZ;IACA,OAAO;QAAE8D;QAAQC;IAAM;AACzB"}
|