@aglyn/plugins-ai 1.0.0-beta.271 → 1.0.0-beta.273
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 +4 -3
- 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-site-build-page.component.js +18 -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 +41 -0
- package/src/lib/components/ai-site-import-job.component.js +707 -0
- package/src/lib/components/ai-site-import-job.component.js.map +1 -0
- package/src/lib/components/ai-site-import-start.component.d.ts +64 -0
- package/src/lib/components/ai-site-import-start.component.js +335 -0
- package/src/lib/components/ai-site-import-start.component.js.map +1 -0
- package/src/lib/components/ai-site-ownership-proof.component.d.ts +42 -0
- package/src/lib/components/ai-site-ownership-proof.component.js +353 -0
- package/src/lib/components/ai-site-ownership-proof.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +329 -252
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/jobs/ai-empty-site-sweep.d.ts +12 -1
- package/src/lib/jobs/ai-empty-site-sweep.js +15 -3
- package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -0
- package/src/lib/jobs/ai-job-drafts.js +24 -0
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +136 -0
- package/src/lib/jobs/ai-job-import-step.js +809 -0
- package/src/lib/jobs/ai-job-import-step.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +39 -1
- package/src/lib/jobs/ai-job-page-language.js +62 -4
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-page-step.js +26 -4
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.js +8 -4
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +39 -1
- package/src/lib/jobs/ai-job-site-datasets.js +111 -15
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +12 -3
- package/src/lib/jobs/ai-job-site-step.js +85 -11
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +15 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +8 -0
- package/src/lib/jobs/ai-jobs.js +19 -3
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-owner-photos.d.ts +16 -1
- package/src/lib/jobs/ai-layout-owner-photos.js +49 -2
- package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +35 -0
- package/src/lib/jobs/ai-layout-stock-photos.js +114 -43
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-site-late-photos.d.ts +143 -0
- package/src/lib/jobs/ai-site-late-photos.js +486 -0
- package/src/lib/jobs/ai-site-late-photos.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +18 -0
- package/src/lib/jobs/ai-site-publish.js +123 -5
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +61 -3
- package/src/lib/jobs/ai-theme-brand-inputs.js +117 -27
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.d.ts +83 -0
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js +1553 -0
- package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +50 -13
- package/src/lib/layout-language/ai-layout-pictures.js +81 -19
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-credit-estimate.d.ts +20 -0
- package/src/lib/model/ai-credit-estimate.js +13 -0
- package/src/lib/model/ai-credit-estimate.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +2 -2
- package/src/lib/model/ai-job-activity.js +6 -4
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-notice.js +18 -4
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +39 -2
- package/src/lib/model/ai-jobs.types.js +2 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-import-view.d.ts +130 -0
- package/src/lib/model/ai-site-import-view.js +182 -0
- package/src/lib/model/ai-site-import-view.js.map +1 -0
- package/src/lib/model/ai-site-import.d.ts +302 -0
- package/src/lib/model/ai-site-import.js +526 -0
- package/src/lib/model/ai-site-import.js.map +1 -0
- package/src/lib/model/ai-site-photos.d.ts +119 -0
- package/src/lib/model/ai-site-photos.js +128 -0
- package/src/lib/model/ai-site-photos.js.map +1 -0
- package/src/lib/model/ai-site-start.d.ts +22 -9
- package/src/lib/model/ai-site-start.js +64 -23
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +3 -1
- package/src/lib/runtime/ai-doctrine-validators.js +3 -1
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/seo-fields.d.ts +29 -4
- package/src/lib/runtime/seo-fields.js +58 -11
- package/src/lib/runtime/seo-fields.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +11 -0
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/ai-site-import-route.d.ts +89 -0
- package/src/lib/server/ai-site-import-route.js +356 -0
- package/src/lib/server/ai-site-import-route.js.map +1 -0
- package/src/lib/server.js +14 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.d.ts +45 -0
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.js +313 -0
- package/src/lib/site-import/fixtures/site-import-crawl-recordings.js.map +1 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.d.ts +50 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.js +217 -0
- package/src/lib/site-import/fixtures/site-import-wave-recordings.js.map +1 -0
- package/src/lib/site-import/site-import-crawl.d.ts +163 -0
- package/src/lib/site-import/site-import-crawl.js +467 -0
- package/src/lib/site-import/site-import-crawl.js.map +1 -0
- package/src/lib/site-import/site-import-flag.d.ts +23 -0
- package/src/lib/site-import/site-import-flag.js +23 -0
- package/src/lib/site-import/site-import-flag.js.map +1 -0
- package/src/lib/site-import/site-import-html.d.ts +94 -0
- package/src/lib/site-import/site-import-html.js +462 -0
- package/src/lib/site-import/site-import-html.js.map +1 -0
- package/src/lib/site-import/site-import-images.d.ts +54 -0
- package/src/lib/site-import/site-import-images.js +204 -0
- package/src/lib/site-import/site-import-images.js.map +1 -0
- package/src/lib/site-import/site-import-page.d.ts +48 -0
- package/src/lib/site-import/site-import-page.js +150 -0
- package/src/lib/site-import/site-import-page.js.map +1 -0
- package/src/lib/site-import/site-import-review.d.ts +40 -0
- package/src/lib/site-import/site-import-review.js +149 -0
- package/src/lib/site-import/site-import-review.js.map +1 -0
- package/src/lib/site-import/site-import-robots.d.ts +52 -0
- package/src/lib/site-import/site-import-robots.js +126 -0
- package/src/lib/site-import/site-import-robots.js.map +1 -0
- package/src/lib/tools/ai-seo-tool.d.ts +25 -1
- package/src/lib/tools/ai-seo-tool.js +61 -0
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { StockPhotoSearchRequest } from '@aglyn/aglyn/plugin-manager/stock-photo-provider';
|
|
18
|
+
import type { AiLayoutPicturePhoto, AiLayoutPictureRole } from '../layout-language/ai-layout-pictures';
|
|
19
|
+
/**
|
|
20
|
+
* THE PICTURES A SITE START LEFT ON A STARTER PHOTO (AGL-3988).
|
|
21
|
+
*
|
|
22
|
+
* A guided start's pages and its datasets' records are each built in a pass
|
|
23
|
+
* of their own, minutes apart. A picture the stock library could not fill in
|
|
24
|
+
* its pass took a starter photo there: its picks' downloads refused by the
|
|
25
|
+
* library's image hosts in a burst, or no photo of the job's own to take
|
|
26
|
+
* yet. The v1.0.0-beta.271 Laurel Fork Photography start (2026-10-19 23:05Z)
|
|
27
|
+
* filed all twelve "Gallery collections" covers and its home hero under an
|
|
28
|
+
* office team and a desk: pixabay.com's Cloudflare answered 429 to 22
|
|
29
|
+
* downloads while the covers were built, before the job had copied any
|
|
30
|
+
* photo, and to 11 more on the home page, the first page built.
|
|
31
|
+
*
|
|
32
|
+
* Each such picture is written down here, with the picks the hosts refused,
|
|
33
|
+
* on the job, by the machine, in the write that records its pass. The site
|
|
34
|
+
* step's late photo pass (`jobs/ai-site-late-photos.ts`) reads it once every
|
|
35
|
+
* page is built and before the site is published: it asks the refused picks
|
|
36
|
+
* for again, a minute after the refusal, and gives every picture still on a
|
|
37
|
+
* starter one of the photos the job copied. Pure.
|
|
38
|
+
*/
|
|
39
|
+
/** Where a starter photo is served from: the platform's own files, on every site's origin. */
|
|
40
|
+
export declare const AI_SITE_STARTER_PREFIX = "/_static/starter/";
|
|
41
|
+
/** Whether an address is a starter photo's. */
|
|
42
|
+
export declare function aiSiteIsStarterSrc(src: unknown): boolean;
|
|
43
|
+
/** The picks a slot kept in hand whose downloads the library's image hosts refused, to ask for again. */
|
|
44
|
+
export interface AiStockRefusedPicks {
|
|
45
|
+
/** The search they came from, as it was sent: asked again, the provider's cache answers it. */
|
|
46
|
+
request: StockPhotoSearchRequest;
|
|
47
|
+
/** The picks' source keys (`pixabay:5401602`), best first. */
|
|
48
|
+
keys: string[];
|
|
49
|
+
/** When the last of them was refused, ISO. */
|
|
50
|
+
at: string;
|
|
51
|
+
}
|
|
52
|
+
/** A picture of one of the start's pages that holds a starter photo. */
|
|
53
|
+
export interface AiSiteStarterSlot {
|
|
54
|
+
/** The `image` node. */
|
|
55
|
+
image: string;
|
|
56
|
+
/** What the picture was to show, in the slot's own words: a starter's alt text is the starter's. */
|
|
57
|
+
words: string;
|
|
58
|
+
role: AiLayoutPictureRole;
|
|
59
|
+
/** The frame's shape, width over height. */
|
|
60
|
+
aspect: number;
|
|
61
|
+
refused?: AiStockRefusedPicks;
|
|
62
|
+
}
|
|
63
|
+
/** A page of the start with the pictures it left on a starter. */
|
|
64
|
+
export interface AiSiteStarterPage {
|
|
65
|
+
/** The page's draft. */
|
|
66
|
+
screen: string;
|
|
67
|
+
slots: AiSiteStarterSlot[];
|
|
68
|
+
}
|
|
69
|
+
/** A record of a pictured list, as its photo was written. */
|
|
70
|
+
export interface AiSiteListRecord {
|
|
71
|
+
/** The record's name: its first field's value. */
|
|
72
|
+
name: string;
|
|
73
|
+
/** What the list files it under, where it files its records (AGL-3856). */
|
|
74
|
+
category?: string;
|
|
75
|
+
/** The value its photo field holds: a starter's path, or the library photo's CDN path. */
|
|
76
|
+
value: string;
|
|
77
|
+
/** The library photo it shows, with its size and words; absent on a starter. */
|
|
78
|
+
photo?: AiLayoutPicturePhoto;
|
|
79
|
+
refused?: AiStockRefusedPicks;
|
|
80
|
+
}
|
|
81
|
+
/** A pictured list of the start, one of whose records holds a starter photo. */
|
|
82
|
+
export interface AiSiteStarterList {
|
|
83
|
+
/** The dataset. */
|
|
84
|
+
dataset: string;
|
|
85
|
+
/** Its photo field, by id. */
|
|
86
|
+
imageField: string;
|
|
87
|
+
/** The field its records keep what their photo shows in, by id (AGL-3856). */
|
|
88
|
+
altField?: string;
|
|
89
|
+
/** A list of the business's people: a record of it never takes a photo of anything but a person. */
|
|
90
|
+
people?: boolean;
|
|
91
|
+
/** Every record, in order, so a record's neighbors are known. */
|
|
92
|
+
records: AiSiteListRecord[];
|
|
93
|
+
}
|
|
94
|
+
/** What a site job holds on its pictures that took a starter, until its late photo pass. */
|
|
95
|
+
export interface AiSitePhotos {
|
|
96
|
+
pages: AiSiteStarterPage[];
|
|
97
|
+
lists: AiSiteStarterList[];
|
|
98
|
+
/**
|
|
99
|
+
* The late photo passes that ran out of time and handed this back (AGL-3988).
|
|
100
|
+
* The site step publishes on the last pass its bound gives them, whatever
|
|
101
|
+
* the pictures hold then, so a burst that never clears never holds a site
|
|
102
|
+
* back.
|
|
103
|
+
*/
|
|
104
|
+
latePasses?: number;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The job's photos state once a unit reported its own (AGL-3988): a page's
|
|
108
|
+
* or a list's entry replaces the one it had, by its draft or its dataset;
|
|
109
|
+
* every other entry stays. `null` where neither holds anything.
|
|
110
|
+
*/
|
|
111
|
+
export declare function aiSiteMergePhotos(previous: AiSitePhotos | null | undefined, next: Partial<AiSitePhotos> | null | undefined): AiSitePhotos | null;
|
|
112
|
+
/**
|
|
113
|
+
* Whether a site job owes its late photo pass: a page picture or a list
|
|
114
|
+
* record it left on a starter. With `pageIds`, only a picture of one of
|
|
115
|
+
* those pages counts: a page that was not built is never published.
|
|
116
|
+
*/
|
|
117
|
+
export declare function aiSitePhotosOwed(photos: AiSitePhotos | null | undefined, pageIds?: readonly string[]): boolean;
|
|
118
|
+
/** When the newest refusal the state holds was made, in ms; `null` where none was. */
|
|
119
|
+
export declare function aiSitePhotosLastRefusalMs(photos: AiSitePhotos | null | undefined): number | null;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
/**
|
|
18
|
+
* THE PICTURES A SITE START LEFT ON A STARTER PHOTO (AGL-3988).
|
|
19
|
+
*
|
|
20
|
+
* A guided start's pages and its datasets' records are each built in a pass
|
|
21
|
+
* of their own, minutes apart. A picture the stock library could not fill in
|
|
22
|
+
* its pass took a starter photo there: its picks' downloads refused by the
|
|
23
|
+
* library's image hosts in a burst, or no photo of the job's own to take
|
|
24
|
+
* yet. The v1.0.0-beta.271 Laurel Fork Photography start (2026-10-19 23:05Z)
|
|
25
|
+
* filed all twelve "Gallery collections" covers and its home hero under an
|
|
26
|
+
* office team and a desk: pixabay.com's Cloudflare answered 429 to 22
|
|
27
|
+
* downloads while the covers were built, before the job had copied any
|
|
28
|
+
* photo, and to 11 more on the home page, the first page built.
|
|
29
|
+
*
|
|
30
|
+
* Each such picture is written down here, with the picks the hosts refused,
|
|
31
|
+
* on the job, by the machine, in the write that records its pass. The site
|
|
32
|
+
* step's late photo pass (`jobs/ai-site-late-photos.ts`) reads it once every
|
|
33
|
+
* page is built and before the site is published: it asks the refused picks
|
|
34
|
+
* for again, a minute after the refusal, and gives every picture still on a
|
|
35
|
+
* starter one of the photos the job copied. Pure.
|
|
36
|
+
*/ /** Where a starter photo is served from: the platform's own files, on every site's origin. */ export const AI_SITE_STARTER_PREFIX = '/_static/starter/';
|
|
37
|
+
/** Whether an address is a starter photo's. */ export function aiSiteIsStarterSrc(src) {
|
|
38
|
+
return typeof src === 'string' && src.startsWith(AI_SITE_STARTER_PREFIX);
|
|
39
|
+
}
|
|
40
|
+
/** The most of each a job keeps: a site's pages, each one's pictures, and its lists' records. */ const PAGES_MAX = 12;
|
|
41
|
+
const SLOTS_MAX = 16;
|
|
42
|
+
const LISTS_MAX = 8;
|
|
43
|
+
const RECORDS_MAX = 24;
|
|
44
|
+
const KEYS_MAX = 3;
|
|
45
|
+
/** An entry with its refused picks bounded, and none where it has none. */ function withPicks(entry) {
|
|
46
|
+
var _ref;
|
|
47
|
+
var _entry_refused;
|
|
48
|
+
const copy = _extends({}, entry);
|
|
49
|
+
delete copy.refused;
|
|
50
|
+
const keys = (_ref = (_entry_refused = entry.refused) == null ? void 0 : _entry_refused.keys.slice(0, KEYS_MAX)) != null ? _ref : [];
|
|
51
|
+
return entry.refused && keys.length ? _extends({}, copy, {
|
|
52
|
+
refused: _extends({}, entry.refused, {
|
|
53
|
+
keys
|
|
54
|
+
})
|
|
55
|
+
}) : copy;
|
|
56
|
+
}
|
|
57
|
+
/** A page's starters as a job keeps them: bounded, and none without a picture. */ function pageOf(page) {
|
|
58
|
+
const slots = page.slots.slice(0, SLOTS_MAX).map(withPicks);
|
|
59
|
+
return page.screen && slots.length ? {
|
|
60
|
+
screen: page.screen,
|
|
61
|
+
slots
|
|
62
|
+
} : null;
|
|
63
|
+
}
|
|
64
|
+
/** Whether a list still holds a starter on any record. */ function listHoldsStarters(list) {
|
|
65
|
+
return list.records.some((record)=>aiSiteIsStarterSrc(record.value));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The job's photos state once a unit reported its own (AGL-3988): a page's
|
|
69
|
+
* or a list's entry replaces the one it had, by its draft or its dataset;
|
|
70
|
+
* every other entry stays. `null` where neither holds anything.
|
|
71
|
+
*/ export function aiSiteMergePhotos(previous, next) {
|
|
72
|
+
var _ref, _ref1, _ref2, _ref3, _ref4;
|
|
73
|
+
const pages = new Map(((_ref = previous == null ? void 0 : previous.pages) != null ? _ref : []).map((page)=>[
|
|
74
|
+
page.screen,
|
|
75
|
+
page
|
|
76
|
+
]));
|
|
77
|
+
for (const page of (_ref1 = next == null ? void 0 : next.pages) != null ? _ref1 : []){
|
|
78
|
+
const kept = pageOf(page);
|
|
79
|
+
if (kept) pages.set(kept.screen, kept);
|
|
80
|
+
else pages.delete(page.screen);
|
|
81
|
+
}
|
|
82
|
+
const lists = new Map(((_ref2 = previous == null ? void 0 : previous.lists) != null ? _ref2 : []).map((list)=>[
|
|
83
|
+
list.dataset,
|
|
84
|
+
list
|
|
85
|
+
]));
|
|
86
|
+
for (const list of (_ref3 = next == null ? void 0 : next.lists) != null ? _ref3 : []){
|
|
87
|
+
if (list.dataset && list.imageField && listHoldsStarters(list)) {
|
|
88
|
+
lists.set(list.dataset, _extends({}, list, {
|
|
89
|
+
records: list.records.slice(0, RECORDS_MAX).map(withPicks)
|
|
90
|
+
}));
|
|
91
|
+
} else lists.delete(list.dataset);
|
|
92
|
+
}
|
|
93
|
+
const merged = {
|
|
94
|
+
pages: [
|
|
95
|
+
...pages.values()
|
|
96
|
+
].slice(0, PAGES_MAX),
|
|
97
|
+
lists: [
|
|
98
|
+
...lists.values()
|
|
99
|
+
].slice(0, LISTS_MAX)
|
|
100
|
+
};
|
|
101
|
+
const latePasses = (_ref4 = next == null ? void 0 : next.latePasses) != null ? _ref4 : previous == null ? void 0 : previous.latePasses;
|
|
102
|
+
if (latePasses) merged.latePasses = latePasses;
|
|
103
|
+
return merged.pages.length || merged.lists.length ? merged : null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Whether a site job owes its late photo pass: a page picture or a list
|
|
107
|
+
* record it left on a starter. With `pageIds`, only a picture of one of
|
|
108
|
+
* those pages counts: a page that was not built is never published.
|
|
109
|
+
*/ export function aiSitePhotosOwed(photos, pageIds) {
|
|
110
|
+
const built = pageIds ? new Set(pageIds) : null;
|
|
111
|
+
return Boolean((photos == null ? void 0 : photos.pages.some((page)=>page.slots.length && (!built || built.has(page.screen)))) || (photos == null ? void 0 : photos.lists.some(listHoldsStarters)));
|
|
112
|
+
}
|
|
113
|
+
/** When the newest refusal the state holds was made, in ms; `null` where none was. */ export function aiSitePhotosLastRefusalMs(photos) {
|
|
114
|
+
var _ref, _ref1;
|
|
115
|
+
const times = [
|
|
116
|
+
...((_ref = photos == null ? void 0 : photos.pages) != null ? _ref : []).flatMap((page)=>page.slots.map((slot)=>{
|
|
117
|
+
var _slot_refused;
|
|
118
|
+
return (_slot_refused = slot.refused) == null ? void 0 : _slot_refused.at;
|
|
119
|
+
})),
|
|
120
|
+
...((_ref1 = photos == null ? void 0 : photos.lists) != null ? _ref1 : []).flatMap((list)=>list.records.map((record)=>{
|
|
121
|
+
var _record_refused;
|
|
122
|
+
return (_record_refused = record.refused) == null ? void 0 : _record_refused.at;
|
|
123
|
+
}))
|
|
124
|
+
].map((at)=>at ? Date.parse(at) : NaN).filter((ms)=>Number.isFinite(ms));
|
|
125
|
+
return times.length ? Math.max(...times) : null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=ai-site-photos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-site-photos.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { StockPhotoSearchRequest } from '@aglyn/aglyn/plugin-manager/stock-photo-provider'\nimport type { AiLayoutPicturePhoto, AiLayoutPictureRole } from '../layout-language/ai-layout-pictures'\n\n/**\n * THE PICTURES A SITE START LEFT ON A STARTER PHOTO (AGL-3988).\n *\n * A guided start's pages and its datasets' records are each built in a pass\n * of their own, minutes apart. A picture the stock library could not fill in\n * its pass took a starter photo there: its picks' downloads refused by the\n * library's image hosts in a burst, or no photo of the job's own to take\n * yet. The v1.0.0-beta.271 Laurel Fork Photography start (2026-10-19 23:05Z)\n * filed all twelve \"Gallery collections\" covers and its home hero under an\n * office team and a desk: pixabay.com's Cloudflare answered 429 to 22\n * downloads while the covers were built, before the job had copied any\n * photo, and to 11 more on the home page, the first page built.\n *\n * Each such picture is written down here, with the picks the hosts refused,\n * on the job, by the machine, in the write that records its pass. The site\n * step's late photo pass (`jobs/ai-site-late-photos.ts`) reads it once every\n * page is built and before the site is published: it asks the refused picks\n * for again, a minute after the refusal, and gives every picture still on a\n * starter one of the photos the job copied. Pure.\n */\n\n/** Where a starter photo is served from: the platform's own files, on every site's origin. */\nexport const AI_SITE_STARTER_PREFIX = '/_static/starter/'\n\n/** Whether an address is a starter photo's. */\nexport function aiSiteIsStarterSrc(src: unknown): boolean {\n return typeof src === 'string' && src.startsWith(AI_SITE_STARTER_PREFIX)\n}\n\n/** The picks a slot kept in hand whose downloads the library's image hosts refused, to ask for again. */\nexport interface AiStockRefusedPicks {\n /** The search they came from, as it was sent: asked again, the provider's cache answers it. */\n request: StockPhotoSearchRequest\n /** The picks' source keys (`pixabay:5401602`), best first. */\n keys: string[]\n /** When the last of them was refused, ISO. */\n at: string\n}\n\n/** A picture of one of the start's pages that holds a starter photo. */\nexport interface AiSiteStarterSlot {\n /** The `image` node. */\n image: string\n /** What the picture was to show, in the slot's own words: a starter's alt text is the starter's. */\n words: string\n role: AiLayoutPictureRole\n /** The frame's shape, width over height. */\n aspect: number\n refused?: AiStockRefusedPicks\n}\n\n/** A page of the start with the pictures it left on a starter. */\nexport interface AiSiteStarterPage {\n /** The page's draft. */\n screen: string\n slots: AiSiteStarterSlot[]\n}\n\n/** A record of a pictured list, as its photo was written. */\nexport interface AiSiteListRecord {\n /** The record's name: its first field's value. */\n name: string\n /** What the list files it under, where it files its records (AGL-3856). */\n category?: string\n /** The value its photo field holds: a starter's path, or the library photo's CDN path. */\n value: string\n /** The library photo it shows, with its size and words; absent on a starter. */\n photo?: AiLayoutPicturePhoto\n refused?: AiStockRefusedPicks\n}\n\n/** A pictured list of the start, one of whose records holds a starter photo. */\nexport interface AiSiteStarterList {\n /** The dataset. */\n dataset: string\n /** Its photo field, by id. */\n imageField: string\n /** The field its records keep what their photo shows in, by id (AGL-3856). */\n altField?: string\n /** A list of the business's people: a record of it never takes a photo of anything but a person. */\n people?: boolean\n /** Every record, in order, so a record's neighbors are known. */\n records: AiSiteListRecord[]\n}\n\n/** What a site job holds on its pictures that took a starter, until its late photo pass. */\nexport interface AiSitePhotos {\n pages: AiSiteStarterPage[]\n lists: AiSiteStarterList[]\n /**\n * The late photo passes that ran out of time and handed this back (AGL-3988).\n * The site step publishes on the last pass its bound gives them, whatever\n * the pictures hold then, so a burst that never clears never holds a site\n * back.\n */\n latePasses?: number\n}\n\n/** The most of each a job keeps: a site's pages, each one's pictures, and its lists' records. */\nconst PAGES_MAX = 12\nconst SLOTS_MAX = 16\nconst LISTS_MAX = 8\nconst RECORDS_MAX = 24\nconst KEYS_MAX = 3\n\n/** An entry with its refused picks bounded, and none where it has none. */\nfunction withPicks<T extends { refused?: AiStockRefusedPicks }>(entry: T): T {\n const copy = { ...entry }\n delete copy.refused\n const keys = entry.refused?.keys.slice(0, KEYS_MAX) ?? []\n return entry.refused && keys.length ? { ...copy, refused: { ...entry.refused, keys } } : copy\n}\n\n/** A page's starters as a job keeps them: bounded, and none without a picture. */\nfunction pageOf(page: AiSiteStarterPage): AiSiteStarterPage | null {\n const slots = page.slots.slice(0, SLOTS_MAX).map(withPicks)\n return page.screen && slots.length ? { screen: page.screen, slots } : null\n}\n\n/** Whether a list still holds a starter on any record. */\nfunction listHoldsStarters(list: AiSiteStarterList): boolean {\n return list.records.some((record) => aiSiteIsStarterSrc(record.value))\n}\n\n/**\n * The job's photos state once a unit reported its own (AGL-3988): a page's\n * or a list's entry replaces the one it had, by its draft or its dataset;\n * every other entry stays. `null` where neither holds anything.\n */\nexport function aiSiteMergePhotos(\n previous: AiSitePhotos | null | undefined,\n next: Partial<AiSitePhotos> | null | undefined,\n): AiSitePhotos | null {\n const pages = new Map((previous?.pages ?? []).map((page) => [page.screen, page]))\n for (const page of next?.pages ?? []) {\n const kept = pageOf(page)\n if (kept) pages.set(kept.screen, kept)\n else pages.delete(page.screen)\n }\n const lists = new Map((previous?.lists ?? []).map((list) => [list.dataset, list]))\n for (const list of next?.lists ?? []) {\n if (list.dataset && list.imageField && listHoldsStarters(list)) {\n lists.set(list.dataset, { ...list, records: list.records.slice(0, RECORDS_MAX).map(withPicks) })\n } else lists.delete(list.dataset)\n }\n const merged: AiSitePhotos = { pages: [...pages.values()].slice(0, PAGES_MAX), lists: [...lists.values()].slice(0, LISTS_MAX) }\n const latePasses = next?.latePasses ?? previous?.latePasses\n if (latePasses) merged.latePasses = latePasses\n return merged.pages.length || merged.lists.length ? merged : null\n}\n\n/**\n * Whether a site job owes its late photo pass: a page picture or a list\n * record it left on a starter. With `pageIds`, only a picture of one of\n * those pages counts: a page that was not built is never published.\n */\nexport function aiSitePhotosOwed(photos: AiSitePhotos | null | undefined, pageIds?: readonly string[]): boolean {\n const built = pageIds ? new Set(pageIds) : null\n return Boolean(\n photos?.pages.some((page) => page.slots.length && (!built || built.has(page.screen))) || photos?.lists.some(listHoldsStarters),\n )\n}\n\n/** When the newest refusal the state holds was made, in ms; `null` where none was. */\nexport function aiSitePhotosLastRefusalMs(photos: AiSitePhotos | null | undefined): number | null {\n const times = [\n ...(photos?.pages ?? []).flatMap((page) => page.slots.map((slot) => slot.refused?.at)),\n ...(photos?.lists ?? []).flatMap((list) => list.records.map((record) => record.refused?.at)),\n ]\n .map((at) => (at ? Date.parse(at) : NaN))\n .filter((ms) => Number.isFinite(ms))\n return times.length ? Math.max(...times) : null\n}\n"],"names":["AI_SITE_STARTER_PREFIX","aiSiteIsStarterSrc","src","startsWith","PAGES_MAX","SLOTS_MAX","LISTS_MAX","RECORDS_MAX","KEYS_MAX","withPicks","entry","copy","refused","keys","slice","length","pageOf","page","slots","map","screen","listHoldsStarters","list","records","some","record","value","aiSiteMergePhotos","previous","next","pages","Map","kept","set","delete","lists","dataset","imageField","merged","values","latePasses","aiSitePhotosOwed","photos","pageIds","built","Set","Boolean","has","aiSitePhotosLastRefusalMs","times","flatMap","slot","at","Date","parse","NaN","filter","ms","Number","isFinite","Math","max"],"mappings":"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"}
|
|
@@ -186,18 +186,29 @@ export declare function aiSiteStartAnswersFromInputs(inputs: Readonly<Record<str
|
|
|
186
186
|
}): AiSiteStartAnswers | null;
|
|
187
187
|
/**
|
|
188
188
|
* The questions' answers for a site staff are building for a client: what
|
|
189
|
-
* the business does is the kind of site, and
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
189
|
+
* the business does is the kind of site, and the details the intake holds
|
|
190
|
+
* fill the Details step's own fields (AGL-3979, AGL-3995): the phone, the
|
|
191
|
+
* email, the towns as the areas it serves and the hours, each held to that
|
|
192
|
+
* detail's own rule, so one the Details step would refuse is left empty
|
|
193
|
+
* there. Every other answer keeps its default, which the review shows to
|
|
194
|
+
* change. The intake, a refused detail included, also reaches the job
|
|
195
|
+
* through {@link aiSiteStartClientInputs} and {@link aiSiteStartClientBrief}.
|
|
193
196
|
*/
|
|
194
197
|
export declare function aiSiteStartAnswersFromClientBuild(intake: ClientBuildIntake): AiSiteStartAnswers;
|
|
195
198
|
/**
|
|
196
199
|
* The per-site inputs the intake fills, which the guided start's own
|
|
197
200
|
* questions leave empty: the business's name for the header and footer, and
|
|
198
201
|
* the towns it serves as its place.
|
|
202
|
+
*
|
|
203
|
+
* The towns are the place only while no address is given. The Entity reads
|
|
204
|
+
* `city` as the address's own locality wherever there is a street or a
|
|
205
|
+
* postal code (`aiSiteEntity`), so once the answers carry either, `city` is
|
|
206
|
+
* the city the Details step was given with that address, never the towns
|
|
207
|
+
* (AGL-3995). The intake asks no address, so today that is the Details step's
|
|
208
|
+
* own, typed on the review; the towns still reach the site as the areas it
|
|
209
|
+
* serves.
|
|
199
210
|
*/
|
|
200
|
-
export declare function aiSiteStartClientInputs(intake: ClientBuildIntake): Pick<AiSiteJobInputs, 'businessName' | 'city'>;
|
|
211
|
+
export declare function aiSiteStartClientInputs(intake: ClientBuildIntake, answers?: Pick<AiSiteStartAnswers, 'street' | 'postalCode' | 'city'> | null): Pick<AiSiteJobInputs, 'businessName' | 'city'>;
|
|
201
212
|
/**
|
|
202
213
|
* The intake as sentences added to the brief: how to reach the business and
|
|
203
214
|
* when it is open, which the site should say where a visitor looks for them,
|
|
@@ -206,9 +217,11 @@ export declare function aiSiteStartClientInputs(intake: ClientBuildIntake): Pick
|
|
|
206
217
|
*/
|
|
207
218
|
export declare function aiSiteStartClientBrief(intake: ClientBuildIntake): string;
|
|
208
219
|
/**
|
|
209
|
-
* What the review says the client build adds
|
|
210
|
-
* used without it being asked again:
|
|
211
|
-
*
|
|
212
|
-
*
|
|
220
|
+
* What the review says the client build adds beyond its own rows, so staff
|
|
221
|
+
* see the intake is used without it being asked again: the business's name
|
|
222
|
+
* and the owner's own words, which no row shows. The phone, email, towns and
|
|
223
|
+
* hours are the review's Details rows already (AGL-3995), so the note does
|
|
224
|
+
* not say them twice: "Also from the client's details: Harbor Plumbing and
|
|
225
|
+
* the owner's own words."
|
|
213
226
|
*/
|
|
214
227
|
export declare function aiSiteStartClientNote(intake: ClientBuildIntake): string;
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
-
import {
|
|
17
|
+
import { weeklyHoursFromText, weeklyHoursToText } from "@aglyn/shared-ui-jsx/utils/weekly-hours";
|
|
18
|
+
import { AI_SITE_DETAIL_KEYS, AI_SITE_NO_DETAILS, aiSiteDetailProblem, aiSiteDetailsOf } from "./ai-site-details.js";
|
|
18
19
|
import { aiSiteKind, aiSiteKindFor } from "./ai-site-kinds.js";
|
|
19
20
|
import { AI_SITE_INSPIRATION_INPUT, aiSiteInspirationInput, aiSiteInspirationLimit, aiSiteInspirationRefusal, aiSiteInspirationUrl } from "./ai-site-inspiration.js";
|
|
20
21
|
import { AI_SITE_INPUT_MAX_CHARS, AI_SITE_SUBMISSIONS, aiSitePagesBand } from "./ai-site-job.js";
|
|
@@ -304,25 +305,72 @@ import { AI_SITE_INPUT_MAX_CHARS, AI_SITE_SUBMISSIONS, aiSitePagesBand } from ".
|
|
|
304
305
|
const space = cut.lastIndexOf(' ');
|
|
305
306
|
return (space > max / 2 ? cut.slice(0, space) : cut).trim();
|
|
306
307
|
}
|
|
308
|
+
/** A time with am or pm: "9am", "5:30 pm", "12 a.m.". */ const TWELVE_HOUR_TIME = /\b(\d{1,2})(?::(\d{2}))?\s*([ap])\.?m\.?(?![a-z])/gi;
|
|
309
|
+
/**
|
|
310
|
+
* The hours an owner gave, in the shape the Details step's hours picker
|
|
311
|
+
* writes (`Tu-Sa 09:00-17:00`, one set of days a line), so the plan reads
|
|
312
|
+
* them as it reads hours picked there. A time with am or pm is the time it
|
|
313
|
+
* names, "to" joins a range as a dash does, and days with their hours may be
|
|
314
|
+
* set apart by a comma, a semicolon or a line. Whatever still does not read
|
|
315
|
+
* as hours — "8–5", "Sat by appointment" — is left out rather than guessed at.
|
|
316
|
+
*/ function clientBuildHours(hours) {
|
|
317
|
+
const lines = hours.replace(TWELVE_HOUR_TIME, (time, hour, minutes, half)=>{
|
|
318
|
+
const h = Number(hour);
|
|
319
|
+
if (h < 1 || h > 12) return time;
|
|
320
|
+
const clock = h % 12 + (half.toLowerCase() === 'p' ? 12 : 0);
|
|
321
|
+
return `${String(clock).padStart(2, '0')}:${minutes != null ? minutes : '00'}`;
|
|
322
|
+
}).replace(/\s+to\s+/gi, '-').replace(/(\d{1,2}:\d{2})\s*,\s*/g, '$1\n').replace(/;/g, '\n');
|
|
323
|
+
const week = weeklyHoursFromText(lines).hours;
|
|
324
|
+
return week.some((day)=>day.length) ? weeklyHoursToText(week) : '';
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* The towns an intake serves, one a line, as the Details step's "Areas you
|
|
328
|
+
* serve" holds them: "Austin, Round Rock and Pflugerville" is three.
|
|
329
|
+
*/ function clientBuildServiceArea(towns) {
|
|
330
|
+
return towns.split(/\r?\n|[,;]|\s+(?:and|&)\s+/i).map((town)=>town.trim()).filter(Boolean).join('\n');
|
|
331
|
+
}
|
|
307
332
|
/**
|
|
308
333
|
* The questions' answers for a site staff are building for a client: what
|
|
309
|
-
* the business does is the kind of site, and
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
334
|
+
* the business does is the kind of site, and the details the intake holds
|
|
335
|
+
* fill the Details step's own fields (AGL-3979, AGL-3995): the phone, the
|
|
336
|
+
* email, the towns as the areas it serves and the hours, each held to that
|
|
337
|
+
* detail's own rule, so one the Details step would refuse is left empty
|
|
338
|
+
* there. Every other answer keeps its default, which the review shows to
|
|
339
|
+
* change. The intake, a refused detail included, also reaches the job
|
|
340
|
+
* through {@link aiSiteStartClientInputs} and {@link aiSiteStartClientBrief}.
|
|
313
341
|
*/ export function aiSiteStartAnswersFromClientBuild(intake) {
|
|
342
|
+
const details = aiSiteDetailsOf({
|
|
343
|
+
phone: intake.phone,
|
|
344
|
+
email: intake.email,
|
|
345
|
+
serviceArea: clientBuildServiceArea(intake.towns),
|
|
346
|
+
hours: clientBuildHours(intake.hours)
|
|
347
|
+
});
|
|
314
348
|
return _extends({}, AI_SITE_START_ANSWERS, {
|
|
315
|
-
siteType: bounded(intake.whatTheyDo, AI_SITE_INPUT_MAX_CHARS)
|
|
349
|
+
siteType: bounded(intake.whatTheyDo, AI_SITE_INPUT_MAX_CHARS),
|
|
350
|
+
phone: details.phone,
|
|
351
|
+
email: details.email,
|
|
352
|
+
serviceArea: details.serviceArea,
|
|
353
|
+
hours: details.hours
|
|
316
354
|
});
|
|
317
355
|
}
|
|
318
356
|
/**
|
|
319
357
|
* The per-site inputs the intake fills, which the guided start's own
|
|
320
358
|
* questions leave empty: the business's name for the header and footer, and
|
|
321
359
|
* the towns it serves as its place.
|
|
322
|
-
|
|
360
|
+
*
|
|
361
|
+
* The towns are the place only while no address is given. The Entity reads
|
|
362
|
+
* `city` as the address's own locality wherever there is a street or a
|
|
363
|
+
* postal code (`aiSiteEntity`), so once the answers carry either, `city` is
|
|
364
|
+
* the city the Details step was given with that address, never the towns
|
|
365
|
+
* (AGL-3995). The intake asks no address, so today that is the Details step's
|
|
366
|
+
* own, typed on the review; the towns still reach the site as the areas it
|
|
367
|
+
* serves.
|
|
368
|
+
*/ export function aiSiteStartClientInputs(intake, answers) {
|
|
369
|
+
var _ref;
|
|
370
|
+
const addressed = Boolean((answers == null ? void 0 : answers.street.trim()) || (answers == null ? void 0 : answers.postalCode.trim()));
|
|
323
371
|
return {
|
|
324
372
|
businessName: bounded(intake.businessName, AI_SITE_INPUT_MAX_CHARS),
|
|
325
|
-
city: bounded(intake.towns, AI_SITE_INPUT_MAX_CHARS)
|
|
373
|
+
city: bounded(addressed ? (_ref = answers == null ? void 0 : answers.city) != null ? _ref : '' : intake.towns, AI_SITE_INPUT_MAX_CHARS)
|
|
326
374
|
};
|
|
327
375
|
}
|
|
328
376
|
/**
|
|
@@ -352,26 +400,19 @@ import { AI_SITE_INPUT_MAX_CHARS, AI_SITE_SUBMISSIONS, aiSitePagesBand } from ".
|
|
|
352
400
|
return sentences.join(' ');
|
|
353
401
|
}
|
|
354
402
|
/**
|
|
355
|
-
* What the review says the client build adds
|
|
356
|
-
* used without it being asked again:
|
|
357
|
-
*
|
|
358
|
-
*
|
|
403
|
+
* What the review says the client build adds beyond its own rows, so staff
|
|
404
|
+
* see the intake is used without it being asked again: the business's name
|
|
405
|
+
* and the owner's own words, which no row shows. The phone, email, towns and
|
|
406
|
+
* hours are the review's Details rows already (AGL-3995), so the note does
|
|
407
|
+
* not say them twice: "Also from the client's details: Harbor Plumbing and
|
|
408
|
+
* the owner's own words."
|
|
359
409
|
*/ export function aiSiteStartClientNote(intake) {
|
|
360
|
-
const name = intake.businessName.trim();
|
|
361
|
-
const towns = intake.towns.trim();
|
|
362
|
-
const reach = [
|
|
363
|
-
intake.phone.trim() ? 'phone' : '',
|
|
364
|
-
intake.email.trim() ? 'email' : '',
|
|
365
|
-
intake.hours.trim() ? 'hours' : ''
|
|
366
|
-
].filter(Boolean);
|
|
367
410
|
const parts = [
|
|
368
|
-
|
|
369
|
-
reach.length ? `its ${reach.length > 1 ? `${reach.slice(0, -1).join(', ')} and ${reach[reach.length - 1]}` : reach[0]}` : '',
|
|
411
|
+
intake.businessName.trim(),
|
|
370
412
|
intake.ownerWords.trim() ? "the owner's own words" : ''
|
|
371
413
|
].filter(Boolean);
|
|
372
414
|
if (!parts.length) return 'Built from the client’s details.';
|
|
373
|
-
|
|
374
|
-
return `Also from the client’s details: ${list}.`;
|
|
415
|
+
return `Also from the client’s details: ${parts.join(' and ')}.`;
|
|
375
416
|
}
|
|
376
417
|
|
|
377
418
|
//# sourceMappingURL=ai-site-start.js.map
|