@aglyn/plugins-ai 1.0.0-beta.288 → 1.0.0-beta.290
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 +3 -2
- package/src/lib/activity/ai-activity-actions.js +3 -0
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +2 -0
- package/src/lib/components/ai-job-requests.js +2 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +4 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-import-job.component.d.ts +6 -1
- package/src/lib/components/ai-site-import-job.component.js +124 -17
- package/src/lib/components/ai-site-import-job.component.js.map +1 -1
- package/src/lib/components/ai-site-import-start.component.d.ts +6 -0
- package/src/lib/components/ai-site-import-start.component.js +18 -4
- package/src/lib/components/ai-site-import-start.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.js +2 -1
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/jobs/ai-image-reads.d.ts +20 -2
- package/src/lib/jobs/ai-image-reads.js +47 -0
- package/src/lib/jobs/ai-image-reads.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +53 -3
- package/src/lib/jobs/ai-job-import-step.js +386 -64
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +33 -0
- package/src/lib/jobs/ai-job-layout-language.js +75 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.js +8 -3
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +29 -0
- package/src/lib/jobs/ai-job-page-language.js +178 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +3 -0
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-site-import-document.d.ts +117 -0
- package/src/lib/jobs/ai-site-import-document.js +710 -0
- package/src/lib/jobs/ai-site-import-document.js.map +1 -0
- package/src/lib/jobs/ai-site-import-look.d.ts +112 -0
- package/src/lib/jobs/ai-site-import-look.js +402 -0
- package/src/lib/jobs/ai-site-import-look.js.map +1 -0
- package/src/lib/jobs/ai-site-import-publish.d.ts +41 -0
- package/src/lib/jobs/ai-site-import-publish.js +92 -0
- package/src/lib/jobs/ai-site-import-publish.js.map +1 -0
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +27 -2
- package/src/lib/jobs/ai-theme-brand-inputs.js +37 -8
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/layout-language/ai-layout-frame.d.ts +17 -0
- package/src/lib/layout-language/ai-layout-frame.js +73 -2
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -1
- package/src/lib/layout-language/ai-layout-language.d.ts +43 -0
- package/src/lib/layout-language/ai-layout-language.js +199 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +15 -0
- package/src/lib/model/ai-credit-estimate.js +14 -1
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +13 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-import-view.d.ts +47 -3
- package/src/lib/model/ai-site-import-view.js +122 -20
- package/src/lib/model/ai-site-import-view.js.map +1 -1
- package/src/lib/model/ai-site-import.d.ts +232 -7
- package/src/lib/model/ai-site-import.js +365 -35
- package/src/lib/model/ai-site-import.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +18 -0
- package/src/lib/runtime/ai-doctrine-validators.js +16 -5
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +31 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/server/ai-image-reads-route.js +24 -1
- package/src/lib/server/ai-image-reads-route.js.map +1 -1
- package/src/lib/server/ai-jobs-resume.js +7 -1
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/site-import/fixtures/aglyn-com-recordings.d.ts +57 -0
- package/src/lib/site-import/fixtures/aglyn-com-recordings.js +172 -0
- package/src/lib/site-import/fixtures/aglyn-com-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +32 -7
- package/src/lib/site-import/site-import-crawl.js +133 -44
- package/src/lib/site-import/site-import-crawl.js.map +1 -1
- package/src/lib/site-import/site-import-edge-server.d.ts +31 -0
- package/src/lib/site-import/site-import-edge-server.js +31 -0
- package/src/lib/site-import/site-import-edge-server.js.map +1 -0
- package/src/lib/site-import/site-import-edge.d.ts +44 -0
- package/src/lib/site-import/site-import-edge.js +108 -0
- package/src/lib/site-import/site-import-edge.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +73 -3
- package/src/lib/site-import/site-import-html.js +332 -45
- package/src/lib/site-import/site-import-html.js.map +1 -1
- package/src/lib/site-import/site-import-images.d.ts +22 -0
- package/src/lib/site-import/site-import-images.js +78 -2
- package/src/lib/site-import/site-import-images.js.map +1 -1
- package/src/lib/site-import/site-import-page.d.ts +44 -2
- package/src/lib/site-import/site-import-page.js +116 -16
- package/src/lib/site-import/site-import-page.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-import.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * BRING MY SITE: THE WHOLE SITE, IN REVIEWED WAVES (AGL-3976, parent AGL-3973).\n *\n * The `import` job reads a website the workspace has PROVED it owns\n * (AGL-3974's claims) and brings it in as draft pages of a new site. This\n * module is the job's plan of record, pure, so the step, the doors, the\n * console and the specs read one set of rules:\n *\n * - **The inventory.** The crawl's pages, grouped: the MAIN pages (home, the\n * pages its menu links, every top-level page, and any page that belongs to\n * no collection), then each COLLECTION (an address prefix holding at least\n * `AI_SITE_IMPORT_COLLECTION_MIN` pages: `/blog/…`, `/news/…`, a dated\n * `/2024/05/…` archive).\n * - **Never past the plan.** Pages are kept in that order until the site's\n * room under its plan's page count is used; the rest are LEFT OUT, said so\n * with the reason, never dropped silently. So a plan's limit always leaves\n * out a collection's tail before it leaves out a main page.\n * - **Waves.** The kept pages are cut into waves of at most\n * `AI_SITE_IMPORT_WAVE_PAGES`. The first wave is the main pages, with the\n * site's header and footer built first; each later wave is one\n * collection's next slice, so a review is about one kind of page at a\n * time (\"Blog, 1 of 3\"). A wave runs, then waits for the person: nothing\n * starts the next one but them.\n * - **The price.** An imported page is an AI page (`AI_MEASURED_IMPORT_CREDITS`,\n * about 27 credits); a wave is quoted before it starts as its pages and,\n * for the first, its header and footer.\n *=========================================*/\n\nimport { aiCreditRangeOrdered, AI_MEASURED_IMPORT_CREDITS, type AiCreditRange } from './ai-credit-estimate'\nimport type { AiJobItemLedger } from './ai-jobs.types'\n\n/** The job kind. */\nexport const AI_SITE_IMPORT_KIND = 'import' as const\n\n/** The input naming the proved domain the job brings in. */\nexport const AI_SITE_IMPORT_DOMAIN_INPUT = 'domain'\n\n/** The most pages one wave brings in. */\nexport const AI_SITE_IMPORT_WAVE_PAGES = 10\n\n/**\n * The most pages one import reads, whatever the plan allows: an unlimited\n * plan is still one crawl's worth, and the crawl's own time budget may stop\n * sooner (the inventory then says so).\n */\nexport const AI_SITE_IMPORT_MAX_PAGES = 200\n\n/** The fewest pages under one address prefix that make it a collection. */\nexport const AI_SITE_IMPORT_COLLECTION_MIN = 3\n\n/** The most photos brought in from one page. */\nexport const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8\n\n/** The most characters of one page's text a wave keeps for its model answer. */\nexport const AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS = 6_000\n\n/** The most sections one imported page is planned with. */\nexport const AI_SITE_IMPORT_MAX_SECTIONS = 6\n\n/** The most characters a section's name, a page's title or a label keeps. */\nexport const AI_SITE_IMPORT_LABEL_MAX_CHARS = 60\n\n/** A page with fewer words than this and no photo has nothing to bring in. */\nexport const AI_SITE_IMPORT_MIN_WORDS = 5\n\n/** The item ledger slot of the site's header and footer. */\nexport const AI_SITE_IMPORT_FRAME_SLOT = 'frame'\n\n/** The ceiling one imported page reserves: its answer pass and its listing pass, at 50 each. */\nexport const AI_SITE_IMPORT_PAGE_CEILING_CREDITS = 100\n\n/** The ceiling the header and footer reserve: one answer at 50. */\nexport const AI_SITE_IMPORT_FRAME_CEILING_CREDITS = 50\n\n/** The most left-out pages listed by address; the rest are counted. */\nexport const AI_SITE_IMPORT_LEFT_OUT_LISTED = 60\n\n/** A page the crawl read, as the inventory keeps it: no text, only what plans it. */\nexport interface AiSiteImportListedPage {\n url: string\n path: string\n title: string\n foundBy: 'home' | 'sitemap' | 'link'\n words: number\n images: number\n}\n\nexport type AiSiteImportGroupKind = 'main' | 'collection'\n\nexport interface AiSiteImportGroup {\n /** `main`, or `c:<prefix>` for a collection. */\n id: string\n label: string\n kind: AiSiteImportGroupKind\n /** Pages of the group the crawl read, kept or not. */\n pages: number\n}\n\n/** Why a page the site has is not brought in. */\nexport type AiSiteImportLeftOutReason =\n /** Past the site's room under its plan's page count. */\n | 'plan-limit'\n /** robots.txt keeps AglynBot off it. */\n | 'robots'\n /** A sign-in page: refused outright. */\n | 'login-form'\n /** No text and no photo: nothing to bring in. */\n | 'empty'\n /** It could not be read: an error, no answer, or not a web page. */\n | 'unreadable'\n /** The site's bot protection or firewall turned AglynBot away (401, 403 or 429). */\n | 'blocked'\n /** Over the size one page may be. */\n | 'too-large'\n /** It sends visitors to another website. */\n | 'off-site'\n\nexport const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[] = [\n 'plan-limit',\n 'robots',\n 'login-form',\n 'empty',\n 'unreadable',\n 'blocked',\n 'too-large',\n 'off-site',\n]\n\nexport interface AiSiteImportLeftOut {\n url: string\n path: string\n title: string\n reason: AiSiteImportLeftOutReason\n}\n\n/** A photo brought into the site's media library, as a page places it. */\nexport interface AiSiteImportPhoto {\n mediaId: string\n src: string\n width?: number\n height?: number\n alt?: string\n}\n\n/** What a wave's read found on one page, for its model answer. Never on the wire. */\nexport interface AiSiteImportPageRead {\n at: string\n title: string\n /** The page's text in order, cut to `AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS`; fenced only when a prompt reads it. */\n text: string\n /** The page's sections, from its headings. */\n sections: string[]\n /** Its photos' addresses, at most `AI_SITE_IMPORT_IMAGES_PER_PAGE`. */\n images: Array<{ url: string; alt: string }>\n}\n\nexport interface AiSiteImportPage {\n /** The item ledger slot: `p0`, `p1`, … */\n slot: string\n url: string\n path: string\n title: string\n group: string\n wave: number\n /** The id the page's draft is written under, minted with the inventory. */\n draftId: string\n /** The page's address on the new site. */\n slug: string\n words: number\n images: number\n /** Filled by the wave's read; the text is cleared once the page is in. */\n read?: AiSiteImportPageRead | null\n /** Set when the wave's read found the page cannot be brought in after all. */\n refused?: AiSiteImportLeftOutReason | null\n /** Filled once the page's photos are in the library. */\n photos?: {\n brought: number\n /** Already in the library from an earlier page or import: the same file, never a second copy. */\n reused: number\n skipped: number\n assets: AiSiteImportPhoto[]\n } | null\n}\n\nexport type AiSiteImportWaveStatus = 'waiting' | 'running' | 'review' | 'done'\n\nexport interface AiSiteImportWave {\n index: number\n label: string\n /** The group the wave brings in; the first wave's is `main`. */\n group: string\n slots: string[]\n /** Whether the wave builds the site's header and footer first. */\n frame: boolean\n status: AiSiteImportWaveStatus\n startedAt: string | null\n finishedAt: string | null\n}\n\nexport interface AiSiteImportState {\n domain: string\n /** The site's name, as Create site was given it: the header's name. */\n siteName: string\n ownership: { method: string | null; provenAt: number | null; detail: string | null }\n readAt: string\n /** The plan's page count; `null` when it has none. */\n pageLimit: number | null\n /** Pages the site already had when its import read the old site. */\n existingPages: number\n /** Pages the crawl read. */\n found: number\n /** Why the crawl stopped short of the whole site, or `null` when it did not. */\n stoppedBy: string | null\n groups: AiSiteImportGroup[]\n pages: AiSiteImportPage[]\n waves: AiSiteImportWave[]\n /** At most `AI_SITE_IMPORT_LEFT_OUT_LISTED`, by address. */\n leftOut: AiSiteImportLeftOut[]\n /** Every page left out, by reason, listed or not. */\n leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>>\n /** The header and footer's draft id, built in the first wave; `null` when there are no main pages. */\n frame: { draftId: string } | null\n /** The Trust & Safety row this import is logged under. */\n reviewId: string | null\n}\n\n/*------------------------------------------\n * Grouping and waves\n *-----------------------------------------*/\n\nconst clipLabel = (text: string) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > AI_SITE_IMPORT_LABEL_MAX_CHARS\n ? `${line.slice(0, AI_SITE_IMPORT_LABEL_MAX_CHARS - 1).trimEnd()}…`\n : line\n}\n\nconst segmentsOf = (path: string) => String(path ?? '').split('/').filter(Boolean)\n\n/** A collection's prefix for a page address: its first segment, a dated archive's `posts`; `null` for a top-level page. */\nexport function aiSiteImportPrefix(path: string): string | null {\n const segments = segmentsOf(path)\n if (segments.length < 2) return null\n const first = segments[0].toLowerCase()\n return /^\\d{4}$/.test(first) ? 'posts' : first\n}\n\n/** \"news-and-events\" → \"News and events\". */\nexport function aiSiteImportPrefixLabel(prefix: string): string {\n const words = decodeURIComponent(prefix).replace(/[-_+]+/g, ' ').trim()\n return words ? `${words.charAt(0).toUpperCase()}${words.slice(1)}` : 'Pages'\n}\n\n/**\n * The inventory's groups, in the order they are brought in: the main pages,\n * then each collection — one the home page's menu links first, in menu order,\n * then the larger before the smaller.\n */\nexport function aiSiteImportGroups(\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[] = [],\n): { groups: AiSiteImportGroup[]; groupOf: (page: AiSiteImportListedPage) => string } {\n const counts = new Map<string, number>()\n for (const page of pages) {\n const prefix = aiSiteImportPrefix(page.path)\n if (prefix) counts.set(prefix, (counts.get(prefix) ?? 0) + 1)\n }\n const menuPaths = new Set(menu.map((path) => path.replace(/\\/+$/, '') || '/'))\n const collections = new Set([...counts].filter(([, count]) => count >= AI_SITE_IMPORT_COLLECTION_MIN).map(([prefix]) => prefix))\n const groupOf = (page: AiSiteImportListedPage) => {\n const path = page.path.replace(/\\/+$/, '') || '/'\n if (page.foundBy === 'home' || menuPaths.has(path)) return 'main'\n const prefix = aiSiteImportPrefix(page.path)\n return prefix && collections.has(prefix) ? `c:${prefix}` : 'main'\n }\n const menuRank = (prefix: string) => {\n const index = menu.findIndex((path) => {\n const first = segmentsOf(path)[0]?.toLowerCase()\n return first === prefix\n })\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n const sized = new Map<string, number>()\n for (const page of pages) sized.set(groupOf(page), (sized.get(groupOf(page)) ?? 0) + 1)\n const ordered = [...collections]\n .filter((prefix) => (sized.get(`c:${prefix}`) ?? 0) > 0)\n .sort((a, b) => menuRank(a) - menuRank(b) || (sized.get(`c:${b}`) ?? 0) - (sized.get(`c:${a}`) ?? 0) || a.localeCompare(b))\n const groups: AiSiteImportGroup[] = []\n if ((sized.get('main') ?? 0) > 0) groups.push({ id: 'main', label: 'Main pages', kind: 'main', pages: sized.get('main') ?? 0 })\n for (const prefix of ordered) {\n groups.push({ id: `c:${prefix}`, label: aiSiteImportPrefixLabel(prefix), kind: 'collection', pages: sized.get(`c:${prefix}`) ?? 0 })\n }\n return { groups, groupOf }\n}\n\n/**\n * The main pages in the order a visitor meets them: home, then the menu's\n * order, then the rest as the crawl read them. A collection keeps the crawl's\n * order, which is its sitemap's.\n */\nfunction orderedWithin(\n group: string,\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[],\n): AiSiteImportListedPage[] {\n if (group !== 'main') return [...pages]\n const rank = (page: AiSiteImportListedPage) => {\n if (page.foundBy === 'home' || segmentsOf(page.path).length === 0) return -1\n const path = page.path.replace(/\\/+$/, '') || '/'\n const index = menu.findIndex((entry) => (entry.replace(/\\/+$/, '') || '/') === path)\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n return pages\n .map((page, order) => ({ page, order }))\n .sort((a, b) => rank(a.page) - rank(b.page) || a.order - b.order)\n .map(({ page }) => page)\n}\n\n/**\n * The address a page takes on the new site: one segment, as every Aglyn page\n * has, from the last segment of its old address; home is the root. Two pages\n * that would take the same address are told apart by their collection, then\n * by a number. The draft writer still makes it unique on the site itself.\n */\nexport function aiSiteImportSlug(path: string, taken: Set<string>): string {\n const segments = segmentsOf(path)\n if (!segments.length) {\n if (!taken.has('/')) {\n taken.add('/')\n return '/'\n }\n }\n const clean = (value: string) =>\n decodeURIComponent(value)\n .toLowerCase()\n .replace(/\\.(?:html?|php|aspx?)$/, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const last = clean(segments[segments.length - 1] ?? '') || 'page'\n const prefixed = segments.length > 1 ? clean(`${segments[0]}-${segments[segments.length - 1]}`) : last\n for (const candidate of [last, prefixed]) {\n if (candidate && !taken.has(candidate)) {\n taken.add(candidate)\n return candidate\n }\n }\n let attempt = 2\n while (taken.has(`${last}-${attempt}`)) attempt += 1\n const slug = `${last}-${attempt}`\n taken.add(slug)\n return slug\n}\n\nexport interface AiSiteImportInventoryInput {\n domain: string\n siteName: string\n ownership: AiSiteImportState['ownership']\n readAt: string\n pages: readonly AiSiteImportListedPage[]\n /** Addresses the home page's menu links, in its order. */\n menu: readonly string[]\n /** What the crawl could not read, by reason. */\n skipped: ReadonlyArray<{ url: string; reason: AiSiteImportLeftOutReason }>\n /** Skips the crawl only counted. */\n skippedOverflow?: number\n stoppedBy: string | null\n /** The plan's page count; `null` for none. */\n pageLimit: number | null\n existingPages: number\n /** Mints one draft id; the inventory names every page's draft before any is written. */\n mintId: () => string\n reviewId?: string | null\n wavePages?: number\n}\n\n/**\n * The site's inventory cut into waves, every page left out said with its\n * reason. Pure: the step reads the site, this decides what comes in.\n */\nexport function aiSiteImportInventory(input: AiSiteImportInventoryInput): AiSiteImportState {\n const wavePages = Math.max(1, Math.floor(input.wavePages ?? AI_SITE_IMPORT_WAVE_PAGES))\n const leftOut: AiSiteImportLeftOut[] = []\n const leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>> = {}\n const leave = (entry: AiSiteImportLeftOut) => {\n leftOutCount[entry.reason] = (leftOutCount[entry.reason] ?? 0) + 1\n if (leftOut.length < AI_SITE_IMPORT_LEFT_OUT_LISTED) leftOut.push(entry)\n }\n for (const skipped of input.skipped) {\n let path = '/'\n try {\n path = new URL(skipped.url).pathname\n } catch {\n // A malformed address keeps the root as its path.\n }\n leave({ url: skipped.url, path, title: '', reason: skipped.reason })\n }\n if (input.skippedOverflow) leftOutCount['unreadable'] = (leftOutCount['unreadable'] ?? 0) + input.skippedOverflow\n\n const readable = input.pages.filter((page) => {\n if (page.words >= AI_SITE_IMPORT_MIN_WORDS || page.images > 0) return true\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'empty' })\n return false\n })\n const { groups, groupOf } = aiSiteImportGroups(readable, input.menu)\n const limit = input.pageLimit === null || !Number.isFinite(input.pageLimit) ? null : Math.max(0, Math.floor(input.pageLimit))\n let room = limit === null ? Number.POSITIVE_INFINITY : Math.max(0, limit - Math.max(0, input.existingPages))\n const pages: AiSiteImportPage[] = []\n const waves: AiSiteImportWave[] = []\n const taken = new Set<string>()\n for (const group of groups) {\n const members = orderedWithin(group.id, readable.filter((page) => groupOf(page) === group.id), input.menu)\n const kept: AiSiteImportListedPage[] = []\n for (const page of members) {\n if (room > 0) {\n kept.push(page)\n room -= 1\n } else {\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'plan-limit' })\n }\n }\n const parts = Math.ceil(kept.length / wavePages)\n for (let part = 0; part < parts; part += 1) {\n const index = waves.length\n const slice = kept.slice(part * wavePages, (part + 1) * wavePages)\n const slots: string[] = []\n for (const page of slice) {\n const slot = `p${pages.length}`\n slots.push(slot)\n pages.push({\n slot,\n url: page.url,\n path: page.path,\n title: clipLabel(page.title) || aiSiteImportPrefixLabel(segmentsOf(page.path).pop() ?? 'Home'),\n group: group.id,\n wave: index,\n draftId: input.mintId(),\n slug: aiSiteImportSlug(page.path, taken),\n words: page.words,\n images: page.images,\n })\n }\n waves.push({\n index,\n label: parts > 1 ? `${group.label}, ${part + 1} of ${parts}` : group.label,\n group: group.id,\n slots,\n frame: index === 0 && group.kind === 'main',\n status: 'waiting',\n startedAt: null,\n finishedAt: null,\n })\n }\n }\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups,\n pages,\n waves,\n leftOut,\n leftOutCount,\n frame: waves[0]?.frame ? { draftId: input.mintId() } : null,\n reviewId: input.reviewId ?? null,\n }\n}\n\n/** The item ledger an inventory starts: the header and footer, then every page, all pending. */\nexport function aiSiteImportLedger(state: AiSiteImportState): AiJobItemLedger[] {\n const row = (slot: string, op: string, label: string): AiJobItemLedger => ({\n slot,\n op,\n label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n creditsRefunded: 0,\n outputs: [],\n })\n return [\n ...(state.frame ? [row(AI_SITE_IMPORT_FRAME_SLOT, 'layout', 'Header and footer')] : []),\n ...state.pages.map((page) => row(page.slot, 'page', page.title)),\n ]\n}\n\n/*------------------------------------------\n * What runs next\n *-----------------------------------------*/\n\nexport type AiSiteImportWork =\n | { kind: 'inventory' }\n | { kind: 'start-wave'; wave: AiSiteImportWave }\n /** Pages of the running wave its read did not reach before its clock ran out. */\n | { kind: 'read'; wave: AiSiteImportWave; pages: AiSiteImportPage[] }\n | { kind: 'frame'; wave: AiSiteImportWave }\n | { kind: 'images'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'page'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'close-wave'; wave: AiSiteImportWave }\n | { kind: 'done' }\n\nconst OPEN = new Set(['pending', 'running'])\n\n/** Whether an item is still to be built. */\nexport function aiSiteImportItemOpen(items: readonly AiJobItemLedger[] | null | undefined, slot: string): boolean {\n const row = (items ?? []).find((entry) => entry.slot === slot)\n return Boolean(row && OPEN.has(row.status))\n}\n\n/** The wave running now, else `null`. */\nexport function aiSiteImportRunningWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'running') ?? null\n}\n\n/** The next wave a person may start, else `null`. */\nexport function aiSiteImportNextWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n if (!state || aiSiteImportRunningWave(state)) return null\n return state.waves.find((wave) => wave.status === 'waiting') ?? null\n}\n\n/** The wave a person is reviewing, else `null`. */\nexport function aiSiteImportReviewWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'review') ?? null\n}\n\n/** What the next pass of the step does. */\nexport function aiSiteImportNextWork(\n state: AiSiteImportState | null | undefined,\n items: readonly AiJobItemLedger[] | null | undefined,\n): AiSiteImportWork {\n if (!state) return { kind: 'inventory' }\n const running = aiSiteImportRunningWave(state)\n if (!running) {\n const next = aiSiteImportNextWave(state)\n return next ? { kind: 'start-wave', wave: next } : { kind: 'done' }\n }\n // A page the wave's read has not reached yet is read before anything is built.\n const unread = running.slots\n .map((slot) => state.pages.find((page) => page.slot === slot))\n .filter((page): page is AiSiteImportPage => !!page && !page.read && !page.refused && aiSiteImportItemOpen(items, page.slot))\n if (unread.length) return { kind: 'read', wave: running, pages: unread }\n if (running.frame && state.frame && aiSiteImportItemOpen(items, AI_SITE_IMPORT_FRAME_SLOT)) {\n return { kind: 'frame', wave: running }\n }\n for (const slot of running.slots) {\n if (!aiSiteImportItemOpen(items, slot)) continue\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page) continue\n return page.photos ? { kind: 'page', wave: running, page } : { kind: 'images', wave: running, page }\n }\n return { kind: 'close-wave', wave: running }\n}\n\n/*------------------------------------------\n * The state, one pass at a time\n *-----------------------------------------*/\n\nconst withPage = (state: AiSiteImportState, slot: string, patch: Partial<AiSiteImportPage>): AiSiteImportState => ({\n ...state,\n pages: state.pages.map((page) => (page.slot === slot ? { ...page, ...patch } : page)),\n})\n\nconst withWave = (state: AiSiteImportState, index: number, patch: Partial<AiSiteImportWave>): AiSiteImportState => ({\n ...state,\n waves: state.waves.map((wave) => (wave.index === index ? { ...wave, ...patch } : wave)),\n})\n\n/** A wave started: running, with what its read found on each page. */\nexport function aiSiteImportWaveStarted(\n state: AiSiteImportState,\n index: number,\n at: string,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n // The wave reviewed before this one is done: starting the next is the person's say-so.\n const next = withWave(\n { ...state, waves: state.waves.map((wave) => (wave.status === 'review' ? { ...wave, status: 'done' as const } : wave)) },\n index,\n { status: 'running', startedAt: at, finishedAt: null },\n )\n return aiSiteImportPagesRead(next, reads)\n}\n\n/** More of a running wave's pages, read now. */\nexport function aiSiteImportPagesRead(\n state: AiSiteImportState,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n let next = state\n for (const [slot, read] of reads) {\n next =\n typeof read === 'string'\n ? withPage(next, slot, { refused: read, read: null })\n : withPage(next, slot, { read, refused: null, title: clipLabel(read.title) || next.pages.find((page) => page.slot === slot)?.title || '' })\n }\n return next\n}\n\n/** A page's photos are in the library. */\nexport function aiSiteImportPhotosIn(\n state: AiSiteImportState,\n slot: string,\n photos: NonNullable<AiSiteImportPage['photos']>,\n): AiSiteImportState {\n return withPage(state, slot, { photos })\n}\n\n/** A page is in: its text is no longer kept. */\nexport function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page?.read) return state\n return withPage(state, slot, { read: { ...page.read, text: '' } })\n}\n\n/** A wave is in: waiting for its review, or done when no wave follows it. */\nexport function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState {\n const later = state.waves.some((wave) => wave.index > index && wave.status === 'waiting')\n let next = withWave(state, index, { status: later ? 'review' : 'done', finishedAt: at })\n // What the wave read is no longer needed by any model answer.\n for (const slot of state.waves.find((wave) => wave.index === index)?.slots ?? []) next = aiSiteImportPageSettled(next, slot)\n return next\n}\n\n/** The state as the console reads it: every page's read text left off. */\nexport function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null {\n if (!state) return null\n return {\n ...state,\n pages: state.pages.map((page) =>\n page.read ? { ...page, read: { ...page.read, text: '', images: [] } } : page,\n ),\n }\n}\n\n/*------------------------------------------\n * Credits\n *-----------------------------------------*/\n\n/**\n * What bringing in this many pages costs, quoted before it starts (AGL-3976):\n * each page an AI page as measured, its ceiling its two passes, and the\n * site's header and footer once when the wave builds them.\n */\nexport function aiSiteImportCreditRange(pages: number, options: { frame?: boolean } = {}): AiCreditRange {\n const count = Math.max(0, Math.floor(Number.isFinite(pages) ? pages : 0))\n const { page, frame } = AI_MEASURED_IMPORT_CREDITS\n return aiCreditRangeOrdered({\n likely: count * page.median + (options.frame ? frame.median : 0),\n p90: count * page.p90 + (options.frame ? frame.p90 : 0),\n ceiling: count * AI_SITE_IMPORT_PAGE_CEILING_CREDITS + (options.frame ? AI_SITE_IMPORT_FRAME_CEILING_CREDITS : 0),\n })\n}\n\n/** A wave's quote: the pages it has still to bring in, and its header and footer while they are owed. */\nexport function aiSiteImportWaveCreditRange(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const owed = (slot: string) => (items ? aiSiteImportItemOpen(items, slot) : true)\n const pages = wave.slots.filter((slot) => owed(slot) && !state.pages.find((page) => page.slot === slot)?.refused).length\n return aiSiteImportCreditRange(pages, { frame: wave.frame && !!state.frame && owed(AI_SITE_IMPORT_FRAME_SLOT) })\n}\n\n/*------------------------------------------\n * Words\n *-----------------------------------------*/\n\n/** Why a page is not brought in, as a person reads it. */\nexport const AI_SITE_IMPORT_LEFT_OUT_COPY: Readonly<Record<AiSiteImportLeftOutReason, string>> = {\n 'plan-limit': 'Past your plan’s page count. Upgrade for more pages, then bring the rest in.',\n robots: 'The site’s robots.txt asks AglynBot not to read it.',\n 'login-form': 'A sign-in page. Pages that ask for a password are never brought in.',\n empty: 'Nothing to bring in: the page has no text or photos.',\n unreadable: 'It could not be read just now.',\n blocked:\n 'The site’s bot protection or firewall turned AglynBot away. Allow AglynBot in your site’s bot or firewall settings, then bring the site in again.',\n 'too-large': 'Larger than a page can be to bring in.',\n 'off-site': 'It sends visitors to another website.',\n}\n\n/** \"3 pages\" / \"1 page\". */\nexport function aiSiteImportPagesText(count: number): string {\n return `${count.toLocaleString('en-US')} ${count === 1 ? 'page' : 'pages'}`\n}\n\n/** The review a finished wave parks on, or the first wave's go-ahead once the site is read. */\nexport function aiSiteImportWaveMessage(\n state: AiSiteImportState,\n items: readonly AiJobItemLedger[] | null | undefined,\n): string {\n const next = aiSiteImportNextWave(state)\n const reviewing = aiSiteImportReviewWave(state)\n if (!reviewing) {\n const kept = state.pages.length\n return next\n ? `Your site is read: ${aiSiteImportPagesText(kept)} to bring in, in ${state.waves.length === 1 ? 'one wave' : `${state.waves.length} waves`}. Start the first wave when you are ready.`\n : 'Your site is read, and nothing on it can be brought in.'\n }\n const rows = (items ?? []).filter((row) => reviewing.slots.includes(row.slot))\n const done = rows.filter((row) => row.status === 'succeeded' || row.status === 'degraded').length\n const lead = `${reviewing.label} is in: ${aiSiteImportPagesText(done)} ready to review as drafts.`\n return next ? `${lead} Start ${next.label} when you are ready.` : lead\n}\n"],"names":["aiCreditRangeOrdered","AI_MEASURED_IMPORT_CREDITS","AI_SITE_IMPORT_KIND","AI_SITE_IMPORT_DOMAIN_INPUT","AI_SITE_IMPORT_WAVE_PAGES","AI_SITE_IMPORT_MAX_PAGES","AI_SITE_IMPORT_COLLECTION_MIN","AI_SITE_IMPORT_IMAGES_PER_PAGE","AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS","AI_SITE_IMPORT_MAX_SECTIONS","AI_SITE_IMPORT_LABEL_MAX_CHARS","AI_SITE_IMPORT_MIN_WORDS","AI_SITE_IMPORT_FRAME_SLOT","AI_SITE_IMPORT_PAGE_CEILING_CREDITS","AI_SITE_IMPORT_FRAME_CEILING_CREDITS","AI_SITE_IMPORT_LEFT_OUT_LISTED","AI_SITE_IMPORT_LEFT_OUT_REASONS","clipLabel","text","line","String","replace","trim","length","slice","trimEnd","segmentsOf","path","split","filter","Boolean","aiSiteImportPrefix","segments","first","toLowerCase","test","aiSiteImportPrefixLabel","prefix","words","decodeURIComponent","charAt","toUpperCase","aiSiteImportGroups","pages","menu","sized","counts","Map","page","set","get","menuPaths","Set","map","collections","count","groupOf","foundBy","has","menuRank","index","findIndex","Number","POSITIVE_INFINITY","ordered","sort","a","b","localeCompare","groups","push","id","label","kind","orderedWithin","group","rank","entry","order","aiSiteImportSlug","taken","add","clean","value","last","prefixed","candidate","attempt","slug","aiSiteImportInventory","input","leftOutCount","waves","wavePages","Math","max","floor","leftOut","leave","reason","skipped","URL","url","pathname","title","skippedOverflow","readable","images","limit","pageLimit","isFinite","room","existingPages","members","kept","parts","ceil","part","slots","slot","pop","wave","draftId","mintId","frame","status","startedAt","finishedAt","domain","siteName","ownership","readAt","found","stoppedBy","reviewId","aiSiteImportLedger","state","row","op","creditsSpent","creditsRefunded","outputs","OPEN","aiSiteImportItemOpen","items","find","aiSiteImportRunningWave","aiSiteImportNextWave","aiSiteImportReviewWave","aiSiteImportNextWork","running","next","unread","read","refused","photos","withPage","patch","withWave","aiSiteImportWaveStarted","at","reads","aiSiteImportPagesRead","aiSiteImportPhotosIn","aiSiteImportPageSettled","aiSiteImportWaveClosed","later","some","aiSiteImportWire","aiSiteImportCreditRange","options","likely","median","p90","ceiling","aiSiteImportWaveCreditRange","owed","AI_SITE_IMPORT_LEFT_OUT_COPY","robots","empty","unreadable","blocked","aiSiteImportPagesText","toLocaleString","aiSiteImportWaveMessage","reviewing","rows","includes","done","lead"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-import.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * BRING MY SITE: THE WHOLE SITE, IN REVIEWED WAVES (AGL-3976, parent AGL-3973).\n *\n * The `import` job reads a website the workspace has PROVED it owns\n * (AGL-3974's claims) and brings it in as draft pages of a new site. This\n * module is the job's plan of record, pure, so the step, the doors, the\n * console and the specs read one set of rules:\n *\n * - **The inventory.** The crawl's pages, grouped: the MAIN pages (home, the\n * pages its menu links, every top-level page, and any page that belongs to\n * no collection), then each COLLECTION (an address prefix holding at least\n * `AI_SITE_IMPORT_COLLECTION_MIN` pages: `/blog/…`, `/news/…`, a dated\n * `/2024/05/…` archive).\n * - **Never past the plan.** Pages are kept in that order until the site's\n * room under its plan's page count is used; the rest are LEFT OUT, said so\n * with the reason, never dropped silently. So a plan's limit always leaves\n * out a collection's tail before it leaves out a main page.\n * - **Waves.** The kept pages are cut into waves of at most\n * `AI_SITE_IMPORT_WAVE_PAGES`. The first wave is the main pages, with the\n * site's header and footer built first; each later wave is one\n * collection's next slice, so a review is about one kind of page at a\n * time (\"Blog, 1 of 3\"). A wave runs, then waits for the person: nothing\n * starts the next one but them.\n * - **The price.** An imported page is an AI page (`AI_MEASURED_IMPORT_CREDITS`,\n * about 27 credits); a wave is quoted before it starts as its pages and,\n * for the first, its header and footer. Once the step says how each page\n * is built, a page copied as it is costs its listing and a page Aglyn AI\n * arranges its arrangement, and the quote names both (AGL-4083).\n * - **Home first (AGL-4083).** The read's verdict (`aiSiteImportReadVerdict`)\n * stops an import whose home page could not be read before anything is\n * planned, quoted or spent: a site seen without its home page is not the\n * site. The person is told why and checks again.\n *=========================================*/\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport type { SiteImportBlock, SiteImportForm } from '../site-import/site-import-html'\nimport { aiCreditRangeOrdered, aiCreditRangeText, AI_MEASURED_IMPORT_CREDITS, type AiCreditRange } from './ai-credit-estimate'\nimport type { AiJobItemLedger } from './ai-jobs.types'\n\n/** The job kind. */\nexport const AI_SITE_IMPORT_KIND = 'import' as const\n\n/** The input naming the proved domain the job brings in. */\nexport const AI_SITE_IMPORT_DOMAIN_INPUT = 'domain'\n\n/** The most pages one wave brings in. */\nexport const AI_SITE_IMPORT_WAVE_PAGES = 10\n\n/**\n * The most pages one import reads, whatever the plan allows: an unlimited\n * plan is still one crawl's worth, and the crawl's own time budget may stop\n * sooner (the inventory then says so).\n */\nexport const AI_SITE_IMPORT_MAX_PAGES = 200\n\n/** The fewest pages under one address prefix that make it a collection. */\nexport const AI_SITE_IMPORT_COLLECTION_MIN = 3\n\n/** The most photos brought in from one page. */\nexport const AI_SITE_IMPORT_IMAGES_PER_PAGE = 8\n\n/**\n * The most characters of one page's text a wave keeps for the page step's\n * prompt, which reads no more than this; every word is kept on the read's\n * `blocks` (AGL-4083).\n */\nexport const AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS = 6_000\n\n/** The most sections one imported page is planned with: one for every second-level heading (AGL-4083). */\nexport const AI_SITE_IMPORT_MAX_SECTIONS = 24\n\n/** The most blocks of one page a wave's read keeps. */\nexport const AI_SITE_IMPORT_READ_MAX_BLOCKS = 2_000\n\n/** The most characters of one page's blocks a wave's read keeps: a long policy is 40,000. */\nexport const AI_SITE_IMPORT_READ_MAX_CHARS = 120_000\n\n/**\n * The most characters of blocks the reads of one import hold at once: they\n * ride the job document, which Firestore caps at 1 MiB, beside the\n * inventory and the ledger. A wave's later page past it keeps what fits and\n * says it was cut.\n */\nexport const AI_SITE_IMPORT_WAVE_READ_MAX_CHARS = 400_000\n\n/**\n * A page with more words of its own than this is copied as it is, never\n * arranged (AGL-4083): a policy, an article, a long story.\n */\nexport const AI_SITE_IMPORT_PROSE_WORDS = 900\n\n/** The answers bot protection and firewalls turn a crawler away with: asked to sign in, refused, or told to slow down. */\nexport const AI_SITE_IMPORT_BLOCKED_STATUSES: ReadonlySet<number> = new Set([401, 403, 429])\n\n/** The most characters a section's name, a page's title or a label keeps. */\nexport const AI_SITE_IMPORT_LABEL_MAX_CHARS = 60\n\n/** A page with fewer words than this and no photo has nothing to bring in. */\nexport const AI_SITE_IMPORT_MIN_WORDS = 5\n\n/** The item ledger slot of the site's header and footer. */\nexport const AI_SITE_IMPORT_FRAME_SLOT = 'frame'\n\n/** The ceiling one imported page reserves: its answer pass and its listing pass, at 50 each. */\nexport const AI_SITE_IMPORT_PAGE_CEILING_CREDITS = 100\n\n/** The ceiling a page copied as it is reserves: its listing pass alone. */\nexport const AI_SITE_IMPORT_COPIED_CEILING_CREDITS = 50\n\n/** The ceiling the header and footer reserve: one answer at 50. */\nexport const AI_SITE_IMPORT_FRAME_CEILING_CREDITS = 50\n\n/** The most left-out pages listed by address; the rest are counted. */\nexport const AI_SITE_IMPORT_LEFT_OUT_LISTED = 60\n\n/** A page the crawl read, as the inventory keeps it: no text, only what plans it. */\nexport interface AiSiteImportListedPage {\n url: string\n path: string\n title: string\n foundBy: 'home' | 'sitemap' | 'link'\n words: number\n images: number\n}\n\nexport type AiSiteImportGroupKind = 'main' | 'collection'\n\nexport interface AiSiteImportGroup {\n /** `main`, or `c:<prefix>` for a collection. */\n id: string\n label: string\n kind: AiSiteImportGroupKind\n /** Pages of the group the crawl read, kept or not. */\n pages: number\n}\n\n/** Why a page the site has is not brought in. */\nexport type AiSiteImportLeftOutReason =\n /** Past the site's room under its plan's page count. */\n | 'plan-limit'\n /** robots.txt keeps AglynBot off it. */\n | 'robots'\n /** A sign-in page: refused outright. */\n | 'login-form'\n /** No text and no photo: nothing to bring in. */\n | 'empty'\n /** It could not be read: an error, no answer, or not a web page. */\n | 'unreadable'\n /** The site's bot protection or firewall turned AglynBot away (401, 403 or 429). */\n | 'blocked'\n /** Over the size one page may be. */\n | 'too-large'\n /** It sends visitors to another website. */\n | 'off-site'\n /** The read stopped (its clock, its bytes, its page count) before it got to it (AGL-4083). */\n | 'not-reached'\n\nexport const AI_SITE_IMPORT_LEFT_OUT_REASONS: readonly AiSiteImportLeftOutReason[] = [\n 'plan-limit',\n 'robots',\n 'login-form',\n 'empty',\n 'unreadable',\n 'blocked',\n 'too-large',\n 'off-site',\n 'not-reached',\n]\n\n/** The reasons that mean the page was there and AglynBot could not read it: the \"could not be read\" of what was found. */\nexport const AI_SITE_IMPORT_UNREAD_REASONS: readonly AiSiteImportLeftOutReason[] = ['unreadable', 'blocked', 'too-large', 'not-reached']\n\nexport interface AiSiteImportLeftOut {\n url: string\n path: string\n title: string\n reason: AiSiteImportLeftOutReason\n}\n\n/** A photo brought into the site's media library, as a page places it. */\nexport interface AiSiteImportPhoto {\n mediaId: string\n src: string\n width?: number\n height?: number\n alt?: string\n /** The address on the old site it was read from, where a page that is copied places it (AGL-4084). */\n from?: string\n}\n\n/** What a wave's read found on one page, for its model answer. Never on the wire. */\nexport interface AiSiteImportPageRead {\n at: string\n title: string\n /** The page's text in order, cut to `AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS`; fenced only when a prompt reads it. */\n text: string\n /** The page's sections, from its headings. */\n sections: string[]\n /** Its photos' addresses, at most `AI_SITE_IMPORT_IMAGES_PER_PAGE`. */\n images: Array<{ url: string; alt: string }>\n /**\n * Every block of the page's main region, in order (AGL-4083): its words as\n * written, its headings, lists, links and pictures, within\n * `AI_SITE_IMPORT_READ_MAX_BLOCKS` and `AI_SITE_IMPORT_READ_MAX_CHARS`.\n * Untrusted data: fenced wherever a prompt reads it. Emptied once the page\n * is in; absent on a read made before it was kept.\n */\n blocks?: SiteImportBlock[]\n /** Set when `blocks` had to stop short of the page, at its budget or the wave's. */\n cut?: boolean\n /** The words of the page's main region, as the old page has them. */\n words?: number\n /** The forms the page asks visitors to fill in, by their fields. */\n forms?: SiteImportForm[]\n}\n\n/** How a page is built from its old page, as the step chose: its words copied as they are, or arranged by Aglyn AI. */\nexport type AiSiteImportBuild = 'copied' | 'arranged'\n\nexport interface AiSiteImportPage {\n /** The item ledger slot: `p0`, `p1`, … */\n slot: string\n url: string\n path: string\n title: string\n group: string\n wave: number\n /** The id the page's draft is written under, minted with the inventory. */\n draftId: string\n /** The page's address on the new site. */\n slug: string\n words: number\n images: number\n /** Filled by the wave's read; the text is cleared once the page is in. */\n read?: AiSiteImportPageRead | null\n /** Set when the wave's read found the page cannot be brought in after all. */\n refused?: AiSiteImportLeftOutReason | null\n /** How the step builds the page (`aiSiteImportPageBuild`); absent until it chooses, and the page is priced as an AI page. */\n build?: AiSiteImportBuild | null\n /** What came through once the page is in: its words on the new page. */\n brought?: { words: number } | null\n /** Filled once the page's photos are in the library. */\n photos?: {\n brought: number\n /** Already in the library from an earlier page or import: the same file, never a second copy. */\n reused: number\n skipped: number\n assets: AiSiteImportPhoto[]\n } | null\n}\n\nexport type AiSiteImportWaveStatus = 'waiting' | 'running' | 'review' | 'done'\n\nexport interface AiSiteImportWave {\n index: number\n label: string\n /** The group the wave brings in; the first wave's is `main`. */\n group: string\n slots: string[]\n /** Whether the wave builds the site's header and footer first. */\n frame: boolean\n status: AiSiteImportWaveStatus\n startedAt: string | null\n finishedAt: string | null\n}\n\nexport interface AiSiteImportState {\n domain: string\n /** The site's name, as Create site was given it: the header's name. */\n siteName: string\n ownership: { method: string | null; provenAt: number | null; detail: string | null }\n readAt: string\n /** The plan's page count; `null` when it has none. */\n pageLimit: number | null\n /** Pages the site already had when its import read the old site. */\n existingPages: number\n /** Pages the crawl read. */\n found: number\n /** Why the crawl stopped short of the whole site, or `null` when it did not. */\n stoppedBy: string | null\n groups: AiSiteImportGroup[]\n pages: AiSiteImportPage[]\n waves: AiSiteImportWave[]\n /** At most `AI_SITE_IMPORT_LEFT_OUT_LISTED`, by address. */\n leftOut: AiSiteImportLeftOut[]\n /** Every page left out, by reason, listed or not. */\n leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>>\n /** The header and footer's draft id, built in the first wave; `null` when there are no main pages. */\n frame: { draftId: string } | null\n /** The Trust & Safety row this import is logged under. */\n reviewId: string | null\n /**\n * \"Start every remaining wave\" (AGL-4083): each wave closes and the next\n * starts with no review between them; the last wave's close is the stop.\n * Off unless the person chose it.\n */\n autoContinue?: boolean\n /**\n * The wave \"Start every remaining wave\" was chosen at (AGL-4084): the one\n * start the person made, which the day's limit counts; the waves it starts\n * after it are the same start.\n */\n autoContinueFrom?: number | null\n /** What the read found of the site as a whole; a stopping verdict plans nothing. */\n verdict?: AiSiteImportReadVerdict | null\n /** What the home page says of the site as a whole (AGL-4084): its look, its header and footer, its listing. */\n home?: AiSiteImportHome | null\n /** The look pass, once it ran in the first wave (AGL-4084). */\n look?: AiSiteImportLook | null\n}\n\n/**\n * What the home page says of the site as a whole (AGL-4084), kept from the\n * read: the site's listing in its own words, its header's brand as the\n * source draws it, and the words its header and footer carry. Untrusted\n * data from the owner's site, fenced wherever a prompt reads it.\n */\nexport interface AiSiteImportHome {\n url: string\n title: string\n description: string\n ogImage: string | null\n siteName: string\n brand: {\n kind: 'logo-only' | 'logo-and-name' | 'name-only'\n /** The name as the header prints it, where it prints one. */\n text: string\n logo: { url: string; alt: string; type: 'svg' | 'raster' | null } | null\n }\n facts: {\n /** The header's and footer's lines, as written, in order. */\n lines: string[]\n emails: string[]\n phones: string[]\n social: string[]\n address: string\n }\n}\n\n/** What the look pass did (AGL-4084): no model is asked. */\nexport interface AiSiteImportLook {\n at: string\n /** The theme: drawn from the source (`applied`), the owner's kept (`kept`), or the source's look could not be read. */\n theme: 'applied' | 'kept' | 'unread'\n /** The source's colors it was drawn from, the strongest first. */\n colors: string[]\n /** The source's logo, as the library holds it, for the header's brand. */\n logo: AiSiteImportPhoto | null\n /** Whether the site's listing was taken from the home page's own words. */\n seo: boolean\n /** The look's seed, which every page of the import is drawn with. */\n seed: number | null\n /** The look's header arrangement, band rhythm and motion, which the header, the footer and every page are drawn in. */\n style?: { headerAlign?: string; rhythm?: string; motion?: string } | null\n}\n\n/*------------------------------------------\n * The read's verdict: Home first (AGL-4083)\n *-----------------------------------------*/\n\n/**\n * - `home-blocked`: bot protection or a firewall turned AglynBot away from\n * the home page (401, 403 or 429).\n * - `home-unreadable`: the home page could not be read for any other reason,\n * or read as an empty shell (a site that needs a browser to show its words).\n * - `mostly-blocked`: the home page came in, but more than half the site's\n * addresses were turned away; the import goes on, and says so.\n * - `ok`: anything else.\n */\nexport type AiSiteImportReadVerdictKind = 'ok' | 'home-blocked' | 'home-unreadable' | 'mostly-blocked'\n\n/** Why the home page did not come in. */\nexport type AiSiteImportHomeProblem =\n | 'blocked'\n | 'http-error'\n | 'unreachable'\n | 'not-html'\n | 'too-large'\n | 'off-site'\n | 'robots'\n | 'login-form'\n | 'not-reached'\n | 'empty'\n\nexport interface AiSiteImportReadVerdict {\n kind: AiSiteImportReadVerdictKind\n /** Addresses turned away by bot protection or a firewall, the home page among them. */\n blocked: number\n /** The site's addresses the read knew of: the sitemaps', or everything it asked for. */\n total: number\n /** What the home page answered, when it answered with an error. */\n homeStatus: number | null\n homeProblem: AiSiteImportHomeProblem | null\n /** Whose edge turned the home page away, when it said. */\n edge: 'vercel' | 'cloudflare' | null\n}\n\n/** A crawl as the verdict reads it; the crawler's `SiteImportInventory` is one. */\nexport interface AiSiteImportReadInput {\n pages: ReadonlyArray<{\n path: string\n foundBy: string\n blocks: ReadonlyArray<{ kind: string; text?: string; inline?: boolean }>\n images: readonly unknown[]\n }>\n skipped: ReadonlyArray<{ url: string; reason: string; status?: number; edge?: string | null }>\n sitemapUrls: number\n}\n\nconst pathOfUrl = (url: string): string => {\n try {\n return new URL(url).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n return ''\n }\n}\n\nconst wordsOf = (text: string) => (text.match(/[\\p{L}\\p{N}]+/gu) ?? []).length\n\n/** One verdict from what became of the home page and how much of the site was turned away. */\nfunction readVerdictOf(\n home: { problem: AiSiteImportHomeProblem; status: number | null; edge: string | null } | null,\n blocked: number,\n total: number,\n): AiSiteImportReadVerdict {\n const edge: AiSiteImportReadVerdict['edge'] = home?.edge === 'vercel' ? 'vercel' : home?.edge === 'cloudflare' ? 'cloudflare' : null\n const base = { blocked, total, homeStatus: home?.status ?? null, homeProblem: home?.problem ?? null, edge }\n if (home) return { kind: home.problem === 'blocked' ? 'home-blocked' : 'home-unreadable', ...base }\n return { kind: total > 0 && blocked * 2 > total ? 'mostly-blocked' : 'ok', ...base }\n}\n\n/** What a crawler skip of the home page means for it. */\nfunction homeProblemOfSkip(reason: string, status: number | undefined): AiSiteImportHomeProblem {\n switch (reason) {\n case 'http-error':\n return status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(status) ? 'blocked' : 'http-error'\n case 'off-origin-redirect':\n return 'off-site'\n case 'not-html':\n case 'too-large':\n case 'robots':\n case 'login-form':\n case 'not-reached':\n return reason\n default:\n return 'unreachable'\n }\n}\n\n/**\n * The read's verdict (AGL-4083), pure over the crawl: whether the home page\n * came in, and how much of the site bot protection turned away. An import\n * whose verdict stops (`aiSiteImportReadStops`) plans no wave, quotes\n * nothing and spends nothing: the person is told why and checks again.\n */\nexport function aiSiteImportReadVerdict(crawled: AiSiteImportReadInput): AiSiteImportReadVerdict {\n const blocked = crawled.skipped.filter(\n (entry) => entry.reason === 'http-error' && entry.status !== undefined && AI_SITE_IMPORT_BLOCKED_STATUSES.has(entry.status),\n ).length\n const total = Math.max(crawled.sitemapUrls, crawled.pages.length + crawled.skipped.length)\n const page = crawled.pages.find((entry) => entry.foundBy === 'home') ?? crawled.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const words = page.blocks.reduce((sum, block) => sum + (block.kind === 'image' || block.inline ? 0 : wordsOf(block.text ?? '')), 0)\n const empty = words < AI_SITE_IMPORT_MIN_WORDS && page.images.length === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = crawled.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n return readVerdictOf(\n skip\n ? { problem: homeProblemOfSkip(skip.reason, skip.status), status: skip.status ?? null, edge: skip.edge ?? null }\n : { problem: 'not-reached', status: null, edge: null },\n blocked,\n total,\n )\n}\n\n/** Whether a verdict stops the import before anything is planned: its home page is not in. */\nexport function aiSiteImportReadStops(verdict: AiSiteImportReadVerdict | null | undefined): boolean {\n return verdict?.kind === 'home-blocked' || verdict?.kind === 'home-unreadable'\n}\n\n/** The review an import that stopped at its read waits under: `blocked` or `unreadable`; `null` when it goes on. */\nexport function aiSiteImportReadVerdictReason(verdict: AiSiteImportReadVerdict | null | undefined): 'blocked' | 'unreadable' | null {\n if (verdict?.kind === 'home-blocked') return 'blocked'\n if (verdict?.kind === 'home-unreadable') return 'unreadable'\n return null\n}\n\n/** Where a site owner lets AglynBot through, on the edges that name themselves. */\nexport const AI_SITE_IMPORT_EDGE_HINTS: Readonly<Record<'vercel' | 'cloudflare', string>> = {\n vercel: 'Your site answers through Vercel: in its Firewall, under Rules, add a bypass rule for the user agent AglynBot.',\n cloudflare:\n 'Your site answers through Cloudflare: under Security, WAF, add a custom rule that skips the checks when the user agent contains AglynBot.',\n}\n\nconst NOTHING_SPENT = 'Nothing was brought in and no AI credits were used.'\n\n/** Why the home page could not be read, inside the sentence that says so. */\nfunction homeProblemText(verdict: AiSiteImportReadVerdict): string {\n switch (verdict.homeProblem) {\n case 'http-error':\n return verdict.homeStatus ? `it answered with an error, ${verdict.homeStatus}` : 'it answered with an error'\n case 'not-html':\n return 'it did not answer with a web page'\n case 'too-large':\n return 'it is larger than a page can be to bring in'\n case 'off-site':\n return 'it sends visitors to another website'\n case 'not-reached':\n return 'the read ran out of time before it'\n default:\n return 'it did not answer'\n }\n}\n\n/**\n * What the person is told about the read (AGL-4083), in console words: why\n * nothing was brought in and what to do, or that most of the site was turned\n * away. `null` when there is nothing to say.\n */\nexport function aiSiteImportReadVerdictMessage(verdict: AiSiteImportReadVerdict | null | undefined, domain: string): string | null {\n if (!verdict) return null\n if (verdict.kind === 'home-blocked') {\n const others = Math.max(0, verdict.blocked - 1)\n const more = others ? `, and ${others.toLocaleString('en-US')} other ${others === 1 ? 'page' : 'pages'}` : ''\n const lead = `Your site’s bot protection or firewall turned AglynBot away before it could read your home page${more}. ${NOTHING_SPENT} Allow AglynBot in your site’s bot or firewall settings, then choose Check again.`\n return verdict.edge ? `${lead} ${AI_SITE_IMPORT_EDGE_HINTS[verdict.edge]}` : lead\n }\n if (verdict.kind === 'home-unreadable') {\n switch (verdict.homeProblem) {\n case 'robots':\n return `Your site’s robots.txt asks AglynBot not to read your home page. ${NOTHING_SPENT} Allow AglynBot in your robots.txt, then choose Check again.`\n case 'login-form':\n return `Your home page asks for a password, and pages that do are never brought in. ${NOTHING_SPENT} Open your home page to visitors, then choose Check again.`\n case 'empty':\n return `No text could be read from your home page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet. ${NOTHING_SPENT}`\n default:\n return `Your home page could not be read (${homeProblemText(verdict)}). ${NOTHING_SPENT} Check that ${domain} is up and answers at https://${domain}/, then choose Check again.`\n }\n }\n if (verdict.kind === 'mostly-blocked') {\n return `${verdict.blocked.toLocaleString('en-US')} of ${verdict.total.toLocaleString('en-US')} pages were turned away by your site’s bot protection; see Not brought in.`\n }\n return null\n}\n\n/** The verdict an inventory reads from its own pages and skips, when the step did not hand it the crawl's. */\nfunction inventoryVerdict(input: Pick<AiSiteImportInventoryInput, 'pages' | 'skipped' | 'skippedOverflow'>): AiSiteImportReadVerdict {\n const blocked = input.skipped.filter((entry) => entry.reason === 'blocked').length\n const total = input.pages.length + input.skipped.length + Math.max(0, input.skippedOverflow ?? 0)\n const page = input.pages.find((entry) => entry.foundBy === 'home') ?? input.pages.find((entry) => (entry.path.replace(/\\/+$/, '') || '/') === '/')\n if (page) {\n const empty = page.words < AI_SITE_IMPORT_MIN_WORDS && page.images === 0\n return readVerdictOf(empty ? { problem: 'empty', status: null, edge: null } : null, blocked, total)\n }\n const skip = input.skipped.find((entry) => pathOfUrl(entry.url) === '/')\n const problem: AiSiteImportHomeProblem = !skip\n ? 'not-reached'\n : skip.reason === 'blocked'\n ? 'blocked'\n : skip.reason === 'robots' || skip.reason === 'login-form' || skip.reason === 'too-large' || skip.reason === 'off-site' || skip.reason === 'not-reached' || skip.reason === 'empty'\n ? skip.reason\n : 'unreachable'\n return readVerdictOf({ problem, status: null, edge: null }, blocked, total)\n}\n\n/*------------------------------------------\n * Grouping and waves\n *-----------------------------------------*/\n\nconst clipLabel = (text: string) => {\n const line = String(text ?? '').replace(/\\s+/g, ' ').trim()\n return line.length > AI_SITE_IMPORT_LABEL_MAX_CHARS\n ? `${line.slice(0, AI_SITE_IMPORT_LABEL_MAX_CHARS - 1).trimEnd()}…`\n : line\n}\n\nconst segmentsOf = (path: string) => String(path ?? '').split('/').filter(Boolean)\n\n/** A collection's prefix for a page address: its first segment, a dated archive's `posts`; `null` for a top-level page. */\nexport function aiSiteImportPrefix(path: string): string | null {\n const segments = segmentsOf(path)\n if (segments.length < 2) return null\n const first = segments[0].toLowerCase()\n return /^\\d{4}$/.test(first) ? 'posts' : first\n}\n\n/** \"news-and-events\" → \"News and events\". */\nexport function aiSiteImportPrefixLabel(prefix: string): string {\n const words = decodeURIComponent(prefix).replace(/[-_+]+/g, ' ').trim()\n return words ? `${words.charAt(0).toUpperCase()}${words.slice(1)}` : 'Pages'\n}\n\n/**\n * The inventory's groups, in the order they are brought in: the main pages,\n * then each collection — one the home page's menu links first, in menu order,\n * then the larger before the smaller.\n */\nexport function aiSiteImportGroups(\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[] = [],\n): { groups: AiSiteImportGroup[]; groupOf: (page: AiSiteImportListedPage) => string } {\n const counts = new Map<string, number>()\n for (const page of pages) {\n const prefix = aiSiteImportPrefix(page.path)\n if (prefix) counts.set(prefix, (counts.get(prefix) ?? 0) + 1)\n }\n const menuPaths = new Set(menu.map((path) => path.replace(/\\/+$/, '') || '/'))\n const collections = new Set([...counts].filter(([, count]) => count >= AI_SITE_IMPORT_COLLECTION_MIN).map(([prefix]) => prefix))\n const groupOf = (page: AiSiteImportListedPage) => {\n const path = page.path.replace(/\\/+$/, '') || '/'\n if (page.foundBy === 'home' || menuPaths.has(path)) return 'main'\n const prefix = aiSiteImportPrefix(page.path)\n return prefix && collections.has(prefix) ? `c:${prefix}` : 'main'\n }\n const menuRank = (prefix: string) => {\n const index = menu.findIndex((path) => {\n const first = segmentsOf(path)[0]?.toLowerCase()\n return first === prefix\n })\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n const sized = new Map<string, number>()\n for (const page of pages) sized.set(groupOf(page), (sized.get(groupOf(page)) ?? 0) + 1)\n const ordered = [...collections]\n .filter((prefix) => (sized.get(`c:${prefix}`) ?? 0) > 0)\n .sort((a, b) => menuRank(a) - menuRank(b) || (sized.get(`c:${b}`) ?? 0) - (sized.get(`c:${a}`) ?? 0) || a.localeCompare(b))\n const groups: AiSiteImportGroup[] = []\n if ((sized.get('main') ?? 0) > 0) groups.push({ id: 'main', label: 'Main pages', kind: 'main', pages: sized.get('main') ?? 0 })\n for (const prefix of ordered) {\n groups.push({ id: `c:${prefix}`, label: aiSiteImportPrefixLabel(prefix), kind: 'collection', pages: sized.get(`c:${prefix}`) ?? 0 })\n }\n return { groups, groupOf }\n}\n\n/**\n * The main pages in the order a visitor meets them: home, then the menu's\n * order, then the rest as the crawl read them. A collection keeps the crawl's\n * order, which is its sitemap's.\n */\nfunction orderedWithin(\n group: string,\n pages: readonly AiSiteImportListedPage[],\n menu: readonly string[],\n): AiSiteImportListedPage[] {\n if (group !== 'main') return [...pages]\n const rank = (page: AiSiteImportListedPage) => {\n if (page.foundBy === 'home' || segmentsOf(page.path).length === 0) return -1\n const path = page.path.replace(/\\/+$/, '') || '/'\n const index = menu.findIndex((entry) => (entry.replace(/\\/+$/, '') || '/') === path)\n return index === -1 ? Number.POSITIVE_INFINITY : index\n }\n return pages\n .map((page, order) => ({ page, order }))\n .sort((a, b) => rank(a.page) - rank(b.page) || a.order - b.order)\n .map(({ page }) => page)\n}\n\n/**\n * The address a page takes on the new site: one segment, as every Aglyn page\n * has, from the last segment of its old address; home is the root. Two pages\n * that would take the same address are told apart by their collection, then\n * by a number. The draft writer still makes it unique on the site itself.\n */\nexport function aiSiteImportSlug(path: string, taken: Set<string>): string {\n const segments = segmentsOf(path)\n if (!segments.length) {\n if (!taken.has('/')) {\n taken.add('/')\n return '/'\n }\n }\n const clean = (value: string) =>\n decodeURIComponent(value)\n .toLowerCase()\n .replace(/\\.(?:html?|php|aspx?)$/, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const last = clean(segments[segments.length - 1] ?? '') || 'page'\n const prefixed = segments.length > 1 ? clean(`${segments[0]}-${segments[segments.length - 1]}`) : last\n for (const candidate of [last, prefixed]) {\n if (candidate && !taken.has(candidate)) {\n taken.add(candidate)\n return candidate\n }\n }\n let attempt = 2\n while (taken.has(`${last}-${attempt}`)) attempt += 1\n const slug = `${last}-${attempt}`\n taken.add(slug)\n return slug\n}\n\nexport interface AiSiteImportInventoryInput {\n domain: string\n siteName: string\n ownership: AiSiteImportState['ownership']\n readAt: string\n pages: readonly AiSiteImportListedPage[]\n /** Addresses the home page's menu links, in its order. */\n menu: readonly string[]\n /** What the crawl could not read, by reason. */\n skipped: ReadonlyArray<{ url: string; reason: AiSiteImportLeftOutReason }>\n /** Skips the crawl only counted. */\n skippedOverflow?: number\n stoppedBy: string | null\n /**\n * The read's verdict from the crawl itself (`aiSiteImportReadVerdict`), with\n * the status and edge the home page answered with; read from `pages` and\n * `skipped` when absent.\n */\n verdict?: AiSiteImportReadVerdict | null\n /** The plan's page count; `null` for none. */\n pageLimit: number | null\n existingPages: number\n /** Mints one draft id; the inventory names every page's draft before any is written. */\n mintId: () => string\n reviewId?: string | null\n wavePages?: number\n}\n\n/**\n * The site's inventory cut into waves, every page left out said with its\n * reason. Pure: the step reads the site, this decides what comes in.\n */\nexport function aiSiteImportInventory(input: AiSiteImportInventoryInput): AiSiteImportState {\n const wavePages = Math.max(1, Math.floor(input.wavePages ?? AI_SITE_IMPORT_WAVE_PAGES))\n const limit = input.pageLimit === null || !Number.isFinite(input.pageLimit) ? null : Math.max(0, Math.floor(input.pageLimit))\n const leftOut: AiSiteImportLeftOut[] = []\n const leftOutCount: Partial<Record<AiSiteImportLeftOutReason, number>> = {}\n const leave = (entry: AiSiteImportLeftOut) => {\n leftOutCount[entry.reason] = (leftOutCount[entry.reason] ?? 0) + 1\n if (leftOut.length < AI_SITE_IMPORT_LEFT_OUT_LISTED) leftOut.push(entry)\n }\n // The read asks for a wave's worth past the plan's room, so a page its page\n // budget never reached is past the plan; any other stop is not the plan's.\n const unreached: AiSiteImportLeftOutReason = input.stoppedBy === 'page-budget' && limit !== null ? 'plan-limit' : 'not-reached'\n for (const skipped of input.skipped) {\n let path = '/'\n try {\n path = new URL(skipped.url).pathname\n } catch {\n // A malformed address keeps the root as its path.\n }\n leave({ url: skipped.url, path, title: '', reason: skipped.reason === 'not-reached' ? unreached : skipped.reason })\n }\n if (input.skippedOverflow) leftOutCount['unreadable'] = (leftOutCount['unreadable'] ?? 0) + input.skippedOverflow\n\n // Home first: a site read without its home page plans nothing at all.\n const verdict = input.verdict ?? inventoryVerdict(input)\n if (aiSiteImportReadStops(verdict)) {\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups: [],\n pages: [],\n waves: [],\n leftOut,\n leftOutCount,\n frame: null,\n reviewId: input.reviewId ?? null,\n verdict,\n }\n }\n\n const readable = input.pages.filter((page) => {\n if (page.words >= AI_SITE_IMPORT_MIN_WORDS || page.images > 0) return true\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'empty' })\n return false\n })\n const { groups, groupOf } = aiSiteImportGroups(readable, input.menu)\n let room = limit === null ? Number.POSITIVE_INFINITY : Math.max(0, limit - Math.max(0, input.existingPages))\n const pages: AiSiteImportPage[] = []\n const waves: AiSiteImportWave[] = []\n const taken = new Set<string>()\n for (const group of groups) {\n const members = orderedWithin(group.id, readable.filter((page) => groupOf(page) === group.id), input.menu)\n const kept: AiSiteImportListedPage[] = []\n for (const page of members) {\n if (room > 0) {\n kept.push(page)\n room -= 1\n } else {\n leave({ url: page.url, path: page.path, title: clipLabel(page.title), reason: 'plan-limit' })\n }\n }\n const parts = Math.ceil(kept.length / wavePages)\n for (let part = 0; part < parts; part += 1) {\n const index = waves.length\n const slice = kept.slice(part * wavePages, (part + 1) * wavePages)\n const slots: string[] = []\n for (const page of slice) {\n const slot = `p${pages.length}`\n slots.push(slot)\n pages.push({\n slot,\n url: page.url,\n path: page.path,\n title: clipLabel(page.title) || aiSiteImportPrefixLabel(segmentsOf(page.path).pop() ?? 'Home'),\n group: group.id,\n wave: index,\n draftId: input.mintId(),\n slug: aiSiteImportSlug(page.path, taken),\n words: page.words,\n images: page.images,\n })\n }\n waves.push({\n index,\n label: parts > 1 ? `${group.label}, ${part + 1} of ${parts}` : group.label,\n group: group.id,\n slots,\n frame: index === 0 && group.kind === 'main',\n status: 'waiting',\n startedAt: null,\n finishedAt: null,\n })\n }\n }\n return {\n domain: input.domain,\n siteName: clipLabel(input.siteName) || input.domain,\n ownership: input.ownership,\n readAt: input.readAt,\n pageLimit: limit,\n existingPages: Math.max(0, input.existingPages),\n found: input.pages.length,\n stoppedBy: input.stoppedBy,\n groups,\n pages,\n waves,\n leftOut,\n leftOutCount,\n frame: waves[0]?.frame ? { draftId: input.mintId() } : null,\n reviewId: input.reviewId ?? null,\n verdict,\n }\n}\n\n/** The item ledger an inventory starts: the header and footer, then every page, all pending. */\nexport function aiSiteImportLedger(state: AiSiteImportState): AiJobItemLedger[] {\n const row = (slot: string, op: string, label: string): AiJobItemLedger => ({\n slot,\n op,\n label,\n status: 'pending',\n attempt: 1,\n creditsSpent: 0,\n creditsRefunded: 0,\n outputs: [],\n })\n return [\n ...(state.frame ? [row(AI_SITE_IMPORT_FRAME_SLOT, 'layout', 'Header and footer')] : []),\n ...state.pages.map((page) => row(page.slot, 'page', page.title)),\n ]\n}\n\n/*------------------------------------------\n * What runs next\n *-----------------------------------------*/\n\nexport type AiSiteImportWork =\n | { kind: 'inventory' }\n | { kind: 'start-wave'; wave: AiSiteImportWave }\n /** Pages of the running wave its read did not reach before its clock ran out. */\n | { kind: 'read'; wave: AiSiteImportWave; pages: AiSiteImportPage[] }\n | { kind: 'frame'; wave: AiSiteImportWave }\n | { kind: 'images'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'page'; wave: AiSiteImportWave; page: AiSiteImportPage }\n | { kind: 'close-wave'; wave: AiSiteImportWave }\n | { kind: 'done' }\n\nconst OPEN = new Set(['pending', 'running'])\n\n/** Whether an item is still to be built. */\nexport function aiSiteImportItemOpen(items: readonly AiJobItemLedger[] | null | undefined, slot: string): boolean {\n const row = (items ?? []).find((entry) => entry.slot === slot)\n return Boolean(row && OPEN.has(row.status))\n}\n\n/** The wave running now, else `null`. */\nexport function aiSiteImportRunningWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'running') ?? null\n}\n\n/** The next wave a person may start, else `null`. */\nexport function aiSiteImportNextWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n if (!state || aiSiteImportRunningWave(state) || aiSiteImportReadStops(state.verdict)) return null\n return state.waves.find((wave) => wave.status === 'waiting') ?? null\n}\n\n/** The wave a person is reviewing, else `null`. */\nexport function aiSiteImportReviewWave(state: AiSiteImportState | null | undefined): AiSiteImportWave | null {\n return state?.waves.find((wave) => wave.status === 'review') ?? null\n}\n\n/** What the next pass of the step does. */\nexport function aiSiteImportNextWork(\n state: AiSiteImportState | null | undefined,\n items: readonly AiJobItemLedger[] | null | undefined,\n): AiSiteImportWork {\n // A read that stopped at its home page reads again: the person's Check again.\n if (!state || aiSiteImportReadStops(state.verdict)) return { kind: 'inventory' }\n const running = aiSiteImportRunningWave(state)\n if (!running) {\n const next = aiSiteImportNextWave(state)\n return next ? { kind: 'start-wave', wave: next } : { kind: 'done' }\n }\n // A page the wave's read has not reached yet is read before anything is built.\n const unread = running.slots\n .map((slot) => state.pages.find((page) => page.slot === slot))\n .filter((page): page is AiSiteImportPage => !!page && !page.read && !page.refused && aiSiteImportItemOpen(items, page.slot))\n if (unread.length) return { kind: 'read', wave: running, pages: unread }\n if (running.frame && state.frame && aiSiteImportItemOpen(items, AI_SITE_IMPORT_FRAME_SLOT)) {\n return { kind: 'frame', wave: running }\n }\n for (const slot of running.slots) {\n if (!aiSiteImportItemOpen(items, slot)) continue\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page) continue\n return page.photos ? { kind: 'page', wave: running, page } : { kind: 'images', wave: running, page }\n }\n return { kind: 'close-wave', wave: running }\n}\n\n/*------------------------------------------\n * The state, one pass at a time\n *-----------------------------------------*/\n\nconst withPage = (state: AiSiteImportState, slot: string, patch: Partial<AiSiteImportPage>): AiSiteImportState => ({\n ...state,\n pages: state.pages.map((page) => (page.slot === slot ? { ...page, ...patch } : page)),\n})\n\nconst withWave = (state: AiSiteImportState, index: number, patch: Partial<AiSiteImportWave>): AiSiteImportState => ({\n ...state,\n waves: state.waves.map((wave) => (wave.index === index ? { ...wave, ...patch } : wave)),\n})\n\n/** A wave started: running, with what its read found on each page. */\nexport function aiSiteImportWaveStarted(\n state: AiSiteImportState,\n index: number,\n at: string,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n // The wave reviewed before this one is done: starting the next is the person's say-so.\n const next = withWave(\n { ...state, waves: state.waves.map((wave) => (wave.status === 'review' ? { ...wave, status: 'done' as const } : wave)) },\n index,\n { status: 'running', startedAt: at, finishedAt: null },\n )\n return aiSiteImportPagesRead(next, reads)\n}\n\n/** How many characters a block holds, as the job document stores it. */\nexport function aiSiteImportBlockChars(block: SiteImportBlock): number {\n if (block.kind === 'image') return block.url.length + block.alt.length\n return block.text.length + (block.kind === 'link' ? block.href.length : 0)\n}\n\n/** A page's blocks in order, as many as fit `maxChars` and `maxBlocks`, and whether any had to go. */\nexport function aiSiteImportCapBlocks(\n blocks: readonly SiteImportBlock[],\n maxChars: number,\n maxBlocks: number = AI_SITE_IMPORT_READ_MAX_BLOCKS,\n): { blocks: SiteImportBlock[]; chars: number; cut: boolean } {\n const kept: SiteImportBlock[] = []\n let chars = 0\n for (const block of blocks) {\n const size = aiSiteImportBlockChars(block)\n if (kept.length >= maxBlocks || chars + size > maxChars) return { blocks: kept, chars, cut: true }\n kept.push(block)\n chars += size\n }\n return { blocks: kept, chars, cut: false }\n}\n\nconst readChars = (read: AiSiteImportPageRead | null | undefined) =>\n (read?.blocks ?? []).reduce((total, block) => total + aiSiteImportBlockChars(block), 0)\n\n/**\n * More of a running wave's pages, read now. Their blocks share\n * `AI_SITE_IMPORT_WAVE_READ_MAX_CHARS` with every read still held: a page\n * past it keeps what fits, in order, and is marked `cut`.\n */\nexport function aiSiteImportPagesRead(\n state: AiSiteImportState,\n reads: ReadonlyMap<string, AiSiteImportPageRead | AiSiteImportLeftOutReason>,\n): AiSiteImportState {\n let held = state.pages.filter((page) => !reads.has(page.slot)).reduce((total, page) => total + readChars(page.read), 0)\n let next = state\n for (const [slot, read] of reads) {\n if (typeof read === 'string') {\n next = withPage(next, slot, { refused: read, read: null })\n continue\n }\n let kept = read\n if (read.blocks) {\n const room = Math.max(0, Math.min(AI_SITE_IMPORT_READ_MAX_CHARS, AI_SITE_IMPORT_WAVE_READ_MAX_CHARS - held))\n const capped = aiSiteImportCapBlocks(read.blocks, room)\n held += capped.chars\n kept = { ...read, blocks: capped.blocks, ...(capped.cut || read.cut ? { cut: true } : {}) }\n }\n next = withPage(next, slot, { read: kept, refused: null, title: clipLabel(read.title) || next.pages.find((page) => page.slot === slot)?.title || '' })\n }\n return next\n}\n\n/** A page's photos are in the library. */\nexport function aiSiteImportPhotosIn(\n state: AiSiteImportState,\n slot: string,\n photos: NonNullable<AiSiteImportPage['photos']>,\n): AiSiteImportState {\n return withPage(state, slot, { photos })\n}\n\n/** A page is in: its text and its blocks are no longer kept. */\nexport function aiSiteImportPageSettled(state: AiSiteImportState, slot: string): AiSiteImportState {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!page?.read) return state\n return withPage(state, slot, { read: { ...page.read, text: '', blocks: [] } })\n}\n\n/** A wave is in: waiting for its review, or done when no wave follows it. */\nexport function aiSiteImportWaveClosed(state: AiSiteImportState, index: number, at: string): AiSiteImportState {\n const later = state.waves.some((wave) => wave.index > index && wave.status === 'waiting')\n // Start every remaining wave: no review between waves, the last one's close is the stop.\n let next = withWave(state, index, { status: later && !state.autoContinue ? 'review' : 'done', finishedAt: at })\n // What the wave read is no longer needed by any model answer.\n for (const slot of state.waves.find((wave) => wave.index === index)?.slots ?? []) next = aiSiteImportPageSettled(next, slot)\n return next\n}\n\n/** The state as the console reads it: every page's read text left off. */\nexport function aiSiteImportWire(state: AiSiteImportState | null | undefined): AiSiteImportState | null {\n if (!state) return null\n return {\n ...state,\n pages: state.pages.map((page) =>\n page.read ? { ...page, read: { ...page.read, text: '', images: [], blocks: [], forms: [] } } : page,\n ),\n }\n}\n\n/*------------------------------------------\n * Credits\n *-----------------------------------------*/\n\n/**\n * How a page is built (AGL-4083), decided by code: every page of a collection\n * (a blog, the legal pages, news) and any page longer than\n * `AI_SITE_IMPORT_PROSE_WORDS` is copied as it is; the main pages are\n * arranged by Aglyn AI, their words kept as written.\n */\nexport function aiSiteImportPageBuild(page: Pick<AiSiteImportPage, 'group' | 'words'>): AiSiteImportBuild {\n return page.group !== 'main' || page.words > AI_SITE_IMPORT_PROSE_WORDS ? 'copied' : 'arranged'\n}\n\nconst whole = (value: number | undefined) => Math.max(0, Math.floor(Number.isFinite(value) ? (value as number) : 0))\n\n/**\n * What bringing in this many pages costs, quoted before it starts (AGL-3976):\n * each page an AI page as measured, its ceiling its two passes, and the\n * site's header and footer once when the wave builds them. Pages the step has\n * chosen a build for (AGL-4083) are priced by it: `arranged` at the\n * arrangement answer and its listing, `copied` at the listing alone.\n */\nexport function aiSiteImportCreditRange(\n pages: number,\n options: { frame?: boolean; arranged?: number; copied?: number } = {},\n): AiCreditRange {\n const count = whole(pages)\n const arranged = whole(options.arranged)\n const copied = whole(options.copied)\n const measured = AI_MEASURED_IMPORT_CREDITS\n return aiCreditRangeOrdered({\n likely:\n count * measured.page.median + arranged * measured.arranged.median + copied * measured.copied.median + (options.frame ? measured.frame.median : 0),\n p90: count * measured.page.p90 + arranged * measured.arranged.p90 + copied * measured.copied.p90 + (options.frame ? measured.frame.p90 : 0),\n ceiling:\n (count + arranged) * AI_SITE_IMPORT_PAGE_CEILING_CREDITS +\n copied * AI_SITE_IMPORT_COPIED_CEILING_CREDITS +\n (options.frame ? AI_SITE_IMPORT_FRAME_CEILING_CREDITS : 0),\n })\n}\n\n/** What a wave still owes, by how its pages are built, and whether its header and footer are owed. */\nexport function aiSiteImportWaveOwed(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): { pages: number; arranged: number; copied: number; frame: boolean } {\n const owed = (slot: string) => (items ? aiSiteImportItemOpen(items, slot) : true)\n const counts = { pages: 0, arranged: 0, copied: 0, frame: wave.frame && !!state.frame && owed(AI_SITE_IMPORT_FRAME_SLOT) }\n for (const slot of wave.slots) {\n const page = state.pages.find((entry) => entry.slot === slot)\n if (!owed(slot) || page?.refused) continue\n if (page?.build === 'arranged') counts.arranged += 1\n else if (page?.build === 'copied') counts.copied += 1\n else counts.pages += 1\n }\n return counts\n}\n\n/** A wave's quote: the pages it has still to bring in, and its header and footer while they are owed. */\nexport function aiSiteImportWaveCreditRange(\n state: AiSiteImportState,\n wave: AiSiteImportWave,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const owed = aiSiteImportWaveOwed(state, wave, items)\n return aiSiteImportCreditRange(owed.pages, { frame: owed.frame, arranged: owed.arranged, copied: owed.copied })\n}\n\n/** Every wave still waiting, quoted at once: \"Start every remaining wave\" (AGL-4083). */\nexport function aiSiteImportRemainingCreditRange(\n state: AiSiteImportState,\n items?: readonly AiJobItemLedger[] | null,\n): AiCreditRange {\n const total = { pages: 0, arranged: 0, copied: 0, frame: false }\n for (const wave of state.waves) {\n if (wave.status !== 'waiting') continue\n const owed = aiSiteImportWaveOwed(state, wave, items)\n total.pages += owed.pages\n total.arranged += owed.arranged\n total.copied += owed.copied\n total.frame = total.frame || owed.frame\n }\n return aiSiteImportCreditRange(total.pages, { frame: total.frame, arranged: total.arranged, copied: total.copied })\n}\n\n/** \"About 95 credits (up to 600): 7 pages arranged by Aglyn AI, 3 copied as they are\"; the build is named once the step has chosen. */\nexport function aiSiteImportQuoteText(\n range: AiCreditRange,\n owed: Pick<ReturnType<typeof aiSiteImportWaveOwed>, 'arranged' | 'copied'>,\n): string {\n const parts: string[] = []\n if (owed.arranged) parts.push(`${aiSiteImportPagesText(owed.arranged)} arranged by ${PLATFORM_BRAND_NAME} AI`)\n if (owed.copied) parts.push(`${owed.arranged ? owed.copied.toLocaleString('en-US') : aiSiteImportPagesText(owed.copied)} copied as ${owed.copied === 1 ? 'it is' : 'they are'}`)\n return parts.length ? `${aiCreditRangeText(range)}: ${parts.join(', ')}` : aiCreditRangeText(range)\n}\n\n/*------------------------------------------\n * Words\n *-----------------------------------------*/\n\n/** Why a page is not brought in, as a person reads it. */\nexport const AI_SITE_IMPORT_LEFT_OUT_COPY: Readonly<Record<AiSiteImportLeftOutReason, string>> = {\n 'plan-limit': 'Past your plan’s page count. Upgrade for more pages, then bring the rest in.',\n robots: 'The site’s robots.txt asks AglynBot not to read it.',\n 'login-form': 'A sign-in page. Pages that ask for a password are never brought in.',\n empty:\n 'No text could be read from this page. If your site is built in a way that needs a browser to show its words, it cannot be brought in yet.',\n unreadable: 'It could not be read just now.',\n blocked:\n 'The site’s bot protection or firewall turned AglynBot away. Allow AglynBot in your site’s bot or firewall settings, then bring the site in again.',\n 'too-large': 'Larger than a page can be to bring in.',\n 'off-site': 'It sends visitors to another website.',\n 'not-reached': 'Not reached: the read stopped before it got to this page.',\n}\n\n/** \"3 pages\" / \"1 page\". */\nexport function aiSiteImportPagesText(count: number): string {\n return `${count.toLocaleString('en-US')} ${count === 1 ? 'page' : 'pages'}`\n}\n\n/** The review a finished wave parks on, or the first wave's go-ahead once the site is read. */\nexport function aiSiteImportWaveMessage(\n state: AiSiteImportState,\n items: readonly AiJobItemLedger[] | null | undefined,\n): string {\n // A read that stopped at its home page says why, and what to do.\n if (aiSiteImportReadStops(state.verdict)) return aiSiteImportReadVerdictMessage(state.verdict, state.domain) ?? ''\n const next = aiSiteImportNextWave(state)\n const reviewing = aiSiteImportReviewWave(state)\n if (!reviewing) {\n const kept = state.pages.length\n return next\n ? `Your site is read: ${aiSiteImportPagesText(kept)} to bring in, in ${state.waves.length === 1 ? 'one wave' : `${state.waves.length} waves`}. Start the first wave when you are ready.`\n : 'Your site is read, and nothing on it can be brought in.'\n }\n const rows = (items ?? []).filter((row) => reviewing.slots.includes(row.slot))\n const done = rows.filter((row) => row.status === 'succeeded' || row.status === 'degraded').length\n const lead = `${reviewing.label} is in: ${aiSiteImportPagesText(done)} ready to review as drafts.`\n return next ? `${lead} Start ${next.label} when you are ready.` : lead\n}\n"],"names":["PLATFORM_BRAND_NAME","aiCreditRangeOrdered","aiCreditRangeText","AI_MEASURED_IMPORT_CREDITS","AI_SITE_IMPORT_KIND","AI_SITE_IMPORT_DOMAIN_INPUT","AI_SITE_IMPORT_WAVE_PAGES","AI_SITE_IMPORT_MAX_PAGES","AI_SITE_IMPORT_COLLECTION_MIN","AI_SITE_IMPORT_IMAGES_PER_PAGE","AI_SITE_IMPORT_PAGE_TEXT_MAX_CHARS","AI_SITE_IMPORT_MAX_SECTIONS","AI_SITE_IMPORT_READ_MAX_BLOCKS","AI_SITE_IMPORT_READ_MAX_CHARS","AI_SITE_IMPORT_WAVE_READ_MAX_CHARS","AI_SITE_IMPORT_PROSE_WORDS","AI_SITE_IMPORT_BLOCKED_STATUSES","Set","AI_SITE_IMPORT_LABEL_MAX_CHARS","AI_SITE_IMPORT_MIN_WORDS","AI_SITE_IMPORT_FRAME_SLOT","AI_SITE_IMPORT_PAGE_CEILING_CREDITS","AI_SITE_IMPORT_COPIED_CEILING_CREDITS","AI_SITE_IMPORT_FRAME_CEILING_CREDITS","AI_SITE_IMPORT_LEFT_OUT_LISTED","AI_SITE_IMPORT_LEFT_OUT_REASONS","AI_SITE_IMPORT_UNREAD_REASONS","pathOfUrl","url","URL","pathname","replace","wordsOf","text","match","length","readVerdictOf","home","blocked","total","edge","base","homeStatus","status","homeProblem","problem","kind","homeProblemOfSkip","reason","undefined","has","aiSiteImportReadVerdict","crawled","skip","skipped","filter","entry","Math","max","sitemapUrls","pages","page","find","foundBy","path","words","blocks","reduce","sum","block","inline","empty","images","aiSiteImportReadStops","verdict","aiSiteImportReadVerdictReason","AI_SITE_IMPORT_EDGE_HINTS","vercel","cloudflare","NOTHING_SPENT","homeProblemText","aiSiteImportReadVerdictMessage","domain","others","more","toLocaleString","lead","inventoryVerdict","input","skippedOverflow","clipLabel","line","String","trim","slice","trimEnd","segmentsOf","split","Boolean","aiSiteImportPrefix","segments","first","toLowerCase","test","aiSiteImportPrefixLabel","prefix","decodeURIComponent","charAt","toUpperCase","aiSiteImportGroups","menu","sized","counts","Map","set","get","menuPaths","map","collections","count","groupOf","menuRank","index","findIndex","Number","POSITIVE_INFINITY","ordered","sort","a","b","localeCompare","groups","push","id","label","orderedWithin","group","rank","order","aiSiteImportSlug","taken","add","clean","value","last","prefixed","candidate","attempt","slug","aiSiteImportInventory","leftOutCount","waves","wavePages","floor","limit","pageLimit","isFinite","leftOut","leave","unreached","stoppedBy","title","siteName","ownership","readAt","existingPages","found","frame","reviewId","readable","room","members","kept","parts","ceil","part","slots","slot","pop","wave","draftId","mintId","startedAt","finishedAt","aiSiteImportLedger","state","row","op","creditsSpent","creditsRefunded","outputs","OPEN","aiSiteImportItemOpen","items","aiSiteImportRunningWave","aiSiteImportNextWave","aiSiteImportReviewWave","aiSiteImportNextWork","running","next","unread","read","refused","photos","withPage","patch","withWave","aiSiteImportWaveStarted","at","reads","aiSiteImportPagesRead","aiSiteImportBlockChars","alt","href","aiSiteImportCapBlocks","maxChars","maxBlocks","chars","size","cut","readChars","held","min","capped","aiSiteImportPhotosIn","aiSiteImportPageSettled","aiSiteImportWaveClosed","later","some","autoContinue","aiSiteImportWire","forms","aiSiteImportPageBuild","whole","aiSiteImportCreditRange","options","arranged","copied","measured","likely","median","p90","ceiling","aiSiteImportWaveOwed","owed","build","aiSiteImportWaveCreditRange","aiSiteImportRemainingCreditRange","aiSiteImportQuoteText","range","aiSiteImportPagesText","join","AI_SITE_IMPORT_LEFT_OUT_COPY","robots","unreadable","aiSiteImportWaveMessage","reviewing","rows","includes","done"],"mappings":"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"}
|
|
@@ -125,6 +125,13 @@ export interface AiDoctrineTreeContext extends AiNodeTreeContext {
|
|
|
125
125
|
* draw them.
|
|
126
126
|
*/
|
|
127
127
|
repeatsCompiled?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* The page is its owner's own words, brought in from their website word for
|
|
130
|
+
* word (AGL-4084). Rule 8 does not ask for a dataset for its lists, which
|
|
131
|
+
* are the source's, and rule 14's copy checks (filler, placeholders,
|
|
132
|
+
* unfinished lines) judge copy a generator wrote, never the owner's.
|
|
133
|
+
*/
|
|
134
|
+
ownWords?: boolean;
|
|
128
135
|
/**
|
|
129
136
|
* The site's home screens, by id (AGL-3056): the screen a link falls back
|
|
130
137
|
* to when the site has none for what it promises. Absent: none is known.
|
|
@@ -752,6 +759,17 @@ export interface AiOutputScore {
|
|
|
752
759
|
}
|
|
753
760
|
/** The rule-17 measurements of one validated tree. */
|
|
754
761
|
export declare function scoreAiOutput(tree: AiDoctrineTree, outputKind: AiOutputKind, context?: AiDoctrineTreeContext): AiOutputScore;
|
|
762
|
+
/**
|
|
763
|
+
* The budget of a page that is its owner's own words, brought in from their
|
|
764
|
+
* website word for word (AGL-4084): a long policy or a full price table is
|
|
765
|
+
* as long as the owner wrote it, and is never cut to a generated page's
|
|
766
|
+
* budget. Still far under the 900 KB a page's nodes may be saved at
|
|
767
|
+
* (`NODE_MAP_MAX_BYTES`).
|
|
768
|
+
*/
|
|
769
|
+
export declare const AI_OWN_WORDS_PAGE_BUDGET: {
|
|
770
|
+
readonly nodes: 1500;
|
|
771
|
+
readonly bytes: 300000;
|
|
772
|
+
};
|
|
755
773
|
/**
|
|
756
774
|
* Rule 17. An output over its kind's budget is refused with the figure, the
|
|
757
775
|
* budget and the heaviest parts named, so the re-ask knows where to cut.
|
|
@@ -3275,11 +3275,22 @@ const BUDGET_METRICS = [
|
|
|
3275
3275
|
'fontFamilies',
|
|
3276
3276
|
'emailHtmlBytes'
|
|
3277
3277
|
];
|
|
3278
|
+
/**
|
|
3279
|
+
* The budget of a page that is its owner's own words, brought in from their
|
|
3280
|
+
* website word for word (AGL-4084): a long policy or a full price table is
|
|
3281
|
+
* as long as the owner wrote it, and is never cut to a generated page's
|
|
3282
|
+
* budget. Still far under the 900 KB a page's nodes may be saved at
|
|
3283
|
+
* (`NODE_MAP_MAX_BYTES`).
|
|
3284
|
+
*/ export const AI_OWN_WORDS_PAGE_BUDGET = {
|
|
3285
|
+
nodes: 1500,
|
|
3286
|
+
bytes: 300000
|
|
3287
|
+
};
|
|
3278
3288
|
/**
|
|
3279
3289
|
* Rule 17. An output over its kind's budget is refused with the figure, the
|
|
3280
3290
|
* budget and the heaviest parts named, so the re-ask knows where to cut.
|
|
3281
3291
|
*/ export function detectOverBudget(score, outputKind, tree, context = {}) {
|
|
3282
|
-
|
|
3292
|
+
// A page of its owner's own words weighs what their page does (AGL-4084), within the save ceiling.
|
|
3293
|
+
const budget = context.ownWords && (outputKind === 'page' || outputKind === 'template') ? _extends({}, AI_OUTPUT_BUDGETS[outputKind], AI_OWN_WORDS_PAGE_BUDGET) : AI_OUTPUT_BUDGETS[outputKind];
|
|
3283
3294
|
const violations = [];
|
|
3284
3295
|
for (const metric of BUDGET_METRICS){
|
|
3285
3296
|
const value = score[metric];
|
|
@@ -4001,7 +4012,7 @@ const BUDGET_METRICS = [
|
|
|
4001
4012
|
...detectInvisibleLinks(tree, outputKind),
|
|
4002
4013
|
...detectPageTextOnBands(tree, outputKind),
|
|
4003
4014
|
...detectOffBrandEmail(tree, outputKind, context.brand),
|
|
4004
|
-
...detectTypedData(tree),
|
|
4015
|
+
...context.ownWords ? [] : detectTypedData(tree),
|
|
4005
4016
|
...detectUnfilledInstances(tree, context),
|
|
4006
4017
|
...detectImageSources(tree, context),
|
|
4007
4018
|
...detectUnrelatedScreenLinks(tree, outputKind, context),
|
|
@@ -4009,13 +4020,13 @@ const BUDGET_METRICS = [
|
|
|
4009
4020
|
...detectDocumentStructure(tree, outputKind),
|
|
4010
4021
|
...detectAdHocWidths(tree, outputKind),
|
|
4011
4022
|
...detectUnresponsiveGrids(tree, outputKind, writtenOf),
|
|
4012
|
-
...detectOffVoiceCopy(aiTreeCopy(tree, context), context.framing),
|
|
4013
|
-
...detectPlaceholderTokens(tree, outputKind, context),
|
|
4023
|
+
...context.ownWords ? [] : detectOffVoiceCopy(aiTreeCopy(tree, context), context.framing),
|
|
4024
|
+
...context.ownWords ? [] : detectPlaceholderTokens(tree, outputKind, context),
|
|
4014
4025
|
...detectCutLines(validated.repairs, written, (id)=>{
|
|
4015
4026
|
var _minted_get;
|
|
4016
4027
|
return (_minted_get = minted.get(id)) != null ? _minted_get : id;
|
|
4017
4028
|
}),
|
|
4018
|
-
...detectDanglingWords(tree),
|
|
4029
|
+
...context.ownWords ? [] : detectDanglingWords(tree),
|
|
4019
4030
|
...detectHeavyDocument(tree, outputKind, context),
|
|
4020
4031
|
...detectEmptyItems(tree)
|
|
4021
4032
|
];
|