@aglyn/plugins-ai 1.0.0-beta.264 → 1.0.0-beta.268
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 +18 -18
- package/src/lib/assets/site-styles/beauty.webp +0 -0
- package/src/lib/assets/site-styles/blog.webp +0 -0
- package/src/lib/assets/site-styles/business.webp +0 -0
- package/src/lib/assets/site-styles/coming-soon.webp +0 -0
- package/src/lib/assets/site-styles/education.webp +0 -0
- package/src/lib/assets/site-styles/events.webp +0 -0
- package/src/lib/assets/site-styles/fitness.webp +0 -0
- package/src/lib/assets/site-styles/landing.webp +0 -0
- package/src/lib/assets/site-styles/music.webp +0 -0
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/personal.webp +0 -0
- package/src/lib/assets/site-styles/photography.webp +0 -0
- package/src/lib/assets/site-styles/portfolio.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/realestate.webp +0 -0
- package/src/lib/assets/site-styles/restaurant.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/studio.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/assets/site-styles/wellness.webp +0 -0
- package/src/lib/assets/site-styles/yoga.webp +0 -0
- package/src/lib/components/ai-job-site.d.ts +8 -1
- package/src/lib/components/ai-job-site.js +7 -0
- package/src/lib/components/ai-job-site.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +37 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +12 -0
- package/src/lib/components/ai-site-start-card.component.js +279 -240
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-start-steps.component.d.ts +114 -0
- package/src/lib/components/ai-site-start-steps.component.js +480 -0
- package/src/lib/components/ai-site-start-steps.component.js.map +1 -0
- package/src/lib/components/ai-site-start-tracking.d.ts +122 -0
- package/src/lib/components/ai-site-start-tracking.js +296 -0
- package/src/lib/components/ai-site-start-tracking.js.map +1 -0
- package/src/lib/components/ai-site-style-previews.d.ts +26 -0
- package/src/lib/components/ai-site-style-previews.js +115 -0
- package/src/lib/components/ai-site-style-previews.js.map +1 -0
- package/src/lib/components/assist-jobs-drawer.component.js +24 -2
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +9 -0
- package/src/lib/insights/ai-insight-digest.js +35 -11
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/jobs/ai-empty-site-sweep.d.ts +154 -0
- package/src/lib/jobs/ai-empty-site-sweep.js +346 -0
- package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +9 -0
- package/src/lib/jobs/ai-job-page-language.js +20 -2
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +43 -8
- package/src/lib/jobs/ai-job-plan-step.js +99 -18
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +82 -1
- package/src/lib/jobs/ai-job-site-datasets.js +276 -14
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-jobs-beat.d.ts +11 -3
- package/src/lib/jobs/ai-jobs-beat.js +23 -6
- package/src/lib/jobs/ai-jobs-beat.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +46 -7
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +143 -10
- package/src/lib/jobs/ai-layout-stock-photos.js +1046 -102
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.d.ts +58 -0
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.js +678 -0
- package/src/lib/jobs/fixtures/ai-site-foreign-datasets-recording.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.d.ts +33 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.js +270 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-beta265-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.d.ts +33 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.js +277 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3856-people-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.d.ts +35 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.js +379 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl3923-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.d.ts +31 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.js +208 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-brightvale-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.js +49 -12
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +6 -3
- package/src/lib/layout-language/ai-layout-design.js +19 -13
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +48 -0
- package/src/lib/layout-language/ai-layout-listings.js +91 -5
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +15 -0
- package/src/lib/layout-language/ai-layout-pictures.js +6 -2
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +20 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-home-arcs.d.ts +75 -0
- package/src/lib/model/ai-site-home-arcs.js +741 -0
- package/src/lib/model/ai-site-home-arcs.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +18 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +9 -6
- package/src/lib/model/ai-site-job.js +8 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.js +24 -24
- package/src/lib/model/ai-site-kinds.js.map +1 -1
- package/src/lib/model/ai-site-start-handoff.d.ts +49 -0
- package/src/lib/model/ai-site-start-handoff.js +88 -0
- package/src/lib/model/ai-site-start-handoff.js.map +1 -0
- package/src/lib/model/ai-site-start-steps.d.ts +67 -0
- package/src/lib/model/ai-site-start-steps.js +127 -0
- package/src/lib/model/ai-site-start-steps.js.map +1 -0
- package/src/lib/model/ai-site-start.js +4 -1
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +32 -0
- package/src/lib/runtime/ai-doctrine-validators.js +119 -2
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -1
- package/src/lib/runtime/ai-doctrine.js +9 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-palette.generated.js +5 -2
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +6 -0
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-insight-digest-route.js +2 -2
- package/src/lib/server/ai-insight-digest-route.js.map +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -0
- package/src/lib/usage/assist-free-taste.js.map +1 -1
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import { pluginResourceDraftWriter } from '@aglyn/aglyn/plugin-manager/plugin-resource-drafts';
|
|
18
|
-
import { type AiBuildPlanCreate, type AiBuildPlanScreen } from '../model/ai-build-plan';
|
|
18
|
+
import { type AiBuildPlan, type AiBuildPlanCreate, type AiBuildPlanScreen } from '../model/ai-build-plan';
|
|
19
|
+
import type { AiInventoryDataset, AiSiteInventory } from '../model/ai-site-inventory';
|
|
19
20
|
import type { AiJob, AiJobOutput, AiJobPlan } from '../model/ai-jobs.types';
|
|
20
21
|
import type { AiPlanCapabilities } from '../model/ai-plan-capabilities';
|
|
21
22
|
import { generateAiDataset } from '../runtime/ai-dataset-generation';
|
|
@@ -175,6 +176,8 @@ export declare function aiSiteDatasetRecordPhotos(input: {
|
|
|
175
176
|
name: string;
|
|
176
177
|
/** Each record's name, by position. */
|
|
177
178
|
names: readonly string[];
|
|
179
|
+
/** What each record is filed under, by position ({@link aiSiteDatasetCategoryOf}, AGL-3856); absent for a list that files none. */
|
|
180
|
+
categories?: ReadonlyArray<string | null>;
|
|
178
181
|
pictures: AiSiteDatasetPictures;
|
|
179
182
|
signal?: AbortSignal;
|
|
180
183
|
stockPhotos?: typeof aiLayoutStockPhotoSource;
|
|
@@ -195,6 +198,23 @@ export interface AiSiteDatasetRunnerDeps {
|
|
|
195
198
|
/** Where a pictured dataset's photos come from; the stock library, else the starters, otherwise. */
|
|
196
199
|
photos?: typeof aiSiteDatasetRecordPhotos;
|
|
197
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* What a designed dataset's records are filed under (AGL-3921): its first
|
|
203
|
+
* field whose name says it files them ("Category", "Type", "Session type"),
|
|
204
|
+
* with each record's value, where they hold at least two different values
|
|
205
|
+
* and some record shares one — a list that files every record apart files
|
|
206
|
+
* none. `null` otherwise. Pure.
|
|
207
|
+
*/
|
|
208
|
+
export declare function aiSiteDatasetCategoryOf(dataset: {
|
|
209
|
+
fields: ReadonlyArray<{
|
|
210
|
+
name: string;
|
|
211
|
+
}>;
|
|
212
|
+
records: ReadonlyArray<ReadonlyArray<string | undefined>>;
|
|
213
|
+
}): {
|
|
214
|
+
column: number;
|
|
215
|
+
name: string;
|
|
216
|
+
values: Array<string | null>;
|
|
217
|
+
} | null;
|
|
198
218
|
/**
|
|
199
219
|
* The fields of a designed dataset that its records leave empty on most of
|
|
200
220
|
* them — every record, or more than half — by the id the data plugin gave
|
|
@@ -249,6 +269,11 @@ export declare function aiDatasetListingsOf(datasets: ReadonlyArray<{
|
|
|
249
269
|
imageField?: string | null;
|
|
250
270
|
/** The record's page-address field, where the writer added one (AGL-3727). */
|
|
251
271
|
addressField?: string | null;
|
|
272
|
+
/** What its records are filed under, by position as `records` (AGL-3921): a section named for one value lists its records alone. */
|
|
273
|
+
groups?: {
|
|
274
|
+
field: string;
|
|
275
|
+
values: readonly string[];
|
|
276
|
+
} | null;
|
|
252
277
|
}>, screens: readonly AiLayoutListingScreen[]): AiLayoutListing[];
|
|
253
278
|
/**
|
|
254
279
|
* A `records` listing for each dataset the workspace already keeps that a
|
|
@@ -267,6 +292,62 @@ export declare function aiSiteReusedDatasetListings(input: {
|
|
|
267
292
|
/** The listings the start already has, whose datasets are not listed twice. */
|
|
268
293
|
listed: ReadonlyArray<Pick<AiLayoutListing, 'datasetId'>>;
|
|
269
294
|
}): AiLayoutListing[];
|
|
295
|
+
/**
|
|
296
|
+
* A SITE START NEVER SHOWS ANOTHER SITE'S RECORDS (AGL-3912).
|
|
297
|
+
*
|
|
298
|
+
* Datasets belong to the organization, and so do their records: neither
|
|
299
|
+
* names the site it was written for, and a dataset outlives the site that
|
|
300
|
+
* filled it (the data plugin keeps `visibleTo` and nothing else of a site). A
|
|
301
|
+
* gym's start reused the workspace's "Session packages", "Coaches" and "Gym
|
|
302
|
+
* FAQs", and published a deleted photographer's wedding packages and a
|
|
303
|
+
* deleted studio's coach bio ("Leads coaching at Ironline Strength Studio")
|
|
304
|
+
* with four broken photos; a photographer's start reused another
|
|
305
|
+
* photographer's gallery and drew six broken covers.
|
|
306
|
+
*
|
|
307
|
+
* So a site start reuses a dataset only when its records are this site's to
|
|
308
|
+
* show ({@link aiSiteDatasetIsThisSites}):
|
|
309
|
+
* - it holds no records at all — an empty list, such as the one a form
|
|
310
|
+
* writes its submissions to, is the same list on any site; or
|
|
311
|
+
* - its scope names this site alone (`siteOnly`), so it was made for it; or
|
|
312
|
+
* - this site already serves its records pages of their own (`recordBase`).
|
|
313
|
+
* Anything else — records in a dataset shared with the workspace or with
|
|
314
|
+
* other sites, and a dataset whose records could not be counted — was filled
|
|
315
|
+
* for something else, and the plan is settled in code before the rules read
|
|
316
|
+
* it ({@link aiSettleSiteForeignDatasets}): the reuse becomes a creation of
|
|
317
|
+
* the same name and fields, designed from this site's brief with photos of
|
|
318
|
+
* its own, and every section, record template and form that named it names
|
|
319
|
+
* the creation. Where this job may create no dataset, the reuse comes off
|
|
320
|
+
* instead and each section writes its items out. Never a refusal, and
|
|
321
|
+
* nothing a member is asked.
|
|
322
|
+
*
|
|
323
|
+
* An Assist change to a site that exists keeps reusing what the site lists:
|
|
324
|
+
* this is a site start's rule, and only its plan is settled by it.
|
|
325
|
+
*/
|
|
326
|
+
export declare function aiSiteDatasetIsThisSites(row: Pick<AiInventoryDataset, 'records' | 'siteOnly' | 'recordBase'>): boolean;
|
|
327
|
+
/**
|
|
328
|
+
* A site start's plan with every dataset it reuses that holds another
|
|
329
|
+
* site's records made again for this one (AGL-3912): each the inventory marks
|
|
330
|
+
* `foreign` ({@link aiSiteInventoryWithRecordCounts}, by
|
|
331
|
+
* {@link aiSiteDatasetIsThisSites}). The creation keeps the dataset's name,
|
|
332
|
+
* which rule 7 does not hold against it as a duplicate. Pure; a plan that
|
|
333
|
+
* reuses none comes back as it was.
|
|
334
|
+
*/
|
|
335
|
+
export declare function aiSettleSiteForeignDatasets(plan: AiBuildPlan, inventory: Pick<AiSiteInventory, 'datasets'> | null, capabilities?: AiPlanCapabilities | null): AiBuildPlan;
|
|
336
|
+
/** How a dataset's records are counted: the data plugin's index, which answers `facts.records`. */
|
|
337
|
+
export type AiDatasetRecordCounter = (request: {
|
|
338
|
+
orgId: string;
|
|
339
|
+
id: string;
|
|
340
|
+
}) => Promise<number | null>;
|
|
341
|
+
/** The production counter: the data plugin's `dataset` index, read one dataset at a time. */
|
|
342
|
+
export declare const aiDatasetRecordCount: AiDatasetRecordCounter;
|
|
343
|
+
/**
|
|
344
|
+
* A site start's inventory with how many records each dataset holds
|
|
345
|
+
* (AGL-3912), and each whose records are not this site's to show
|
|
346
|
+
* ({@link aiSiteDatasetIsThisSites}) marked `foreign`. The count is asked
|
|
347
|
+
* once per plan, of every dataset the site sees, all at once; a count that
|
|
348
|
+
* fails leaves `records` out, which reads as another site's. Never throws.
|
|
349
|
+
*/
|
|
350
|
+
export declare function aiSiteInventoryWithRecordCounts(inventory: AiSiteInventory | null, orgId: string, count?: AiDatasetRecordCounter): Promise<AiSiteInventory | null>;
|
|
270
351
|
/**
|
|
271
352
|
* The site's datasets a layout or a page unit is handed (AGL-3616), each a
|
|
272
353
|
* `records` listing with the sections of the plan's pages that name it, once
|
|
@@ -16,7 +16,9 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { checkDatasetQuota } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
18
18
|
import { pluginResourceDraftWriter } from "@aglyn/aglyn/plugin-manager/plugin-resource-drafts";
|
|
19
|
-
import {
|
|
19
|
+
import { pluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
|
|
20
|
+
import { AI_BUILD_PLAN_LIMITS, AI_PLAN_NEW_REF_PREFIX, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
21
|
+
import { AI_TYPED_LIST_MIN_ITEMS } from "../runtime/ai-doctrine-validators.js";
|
|
20
22
|
import { AI_SITE_DATASETS_MAX } from "../model/ai-site-job.js";
|
|
21
23
|
import { AI_STEP_TIERS } from "../providers/catalog.js";
|
|
22
24
|
import { aiModelForStep } from "../providers/routing.js";
|
|
@@ -242,7 +244,9 @@ const limitReview = (message)=>({
|
|
|
242
244
|
placed: null
|
|
243
245
|
}));
|
|
244
246
|
const people = input.pictures === 'people';
|
|
245
|
-
const slots = input.names.map((name, index)=>
|
|
247
|
+
const slots = input.names.map((name, index)=>{
|
|
248
|
+
var _input_categories_index, _input_categories;
|
|
249
|
+
return _extends({
|
|
246
250
|
imageId: `record${index}`,
|
|
247
251
|
frameId: null,
|
|
248
252
|
iconId: null,
|
|
@@ -253,7 +257,14 @@ const limitReview = (message)=>({
|
|
|
253
257
|
role: people ? 'about' : 'gallery',
|
|
254
258
|
// A record's name is a search's words, never what its photo shows (AGL-3856).
|
|
255
259
|
standIn: true
|
|
256
|
-
}
|
|
260
|
+
}, people ? {
|
|
261
|
+
people: true
|
|
262
|
+
} : {}, people ? {} : {
|
|
263
|
+
record: ((_input_categories = input.categories) == null ? void 0 : (_input_categories_index = _input_categories[index]) == null ? void 0 : _input_categories_index.trim()) ? {
|
|
264
|
+
category: input.categories[index].trim()
|
|
265
|
+
} : {}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
257
268
|
const seed = `${job.$id}:records`;
|
|
258
269
|
let found = [];
|
|
259
270
|
try {
|
|
@@ -343,6 +354,51 @@ const limitReview = (message)=>({
|
|
|
343
354
|
})
|
|
344
355
|
});
|
|
345
356
|
}
|
|
357
|
+
/** A field whose name says it files its records: a category, a type, a kind. */ const CATEGORY_FIELD = /^(?:(?:gallery|session|service|event|course|class|product|menu|work|project) )?(?:category|categories|type|kind|collection|genre|group|section|subject|occasion|series)$/i;
|
|
358
|
+
/**
|
|
359
|
+
* What a designed dataset's records are filed under (AGL-3921): its first
|
|
360
|
+
* field whose name says it files them ("Category", "Type", "Session type"),
|
|
361
|
+
* with each record's value, where they hold at least two different values
|
|
362
|
+
* and some record shares one — a list that files every record apart files
|
|
363
|
+
* none. `null` otherwise. Pure.
|
|
364
|
+
*/ export function aiSiteDatasetCategoryOf(dataset) {
|
|
365
|
+
for (const [column, field] of dataset.fields.entries()){
|
|
366
|
+
if (column === 0 || !CATEGORY_FIELD.test(field.name.trim())) continue;
|
|
367
|
+
const values = dataset.records.map((values)=>{
|
|
368
|
+
var _values_column;
|
|
369
|
+
return ((_values_column = values[column]) != null ? _values_column : '').replace(/\s+/g, ' ').trim() || null;
|
|
370
|
+
});
|
|
371
|
+
const distinct = new Set(values.filter((value)=>Boolean(value)).map((value)=>value.toLowerCase()));
|
|
372
|
+
if (distinct.size >= 2 && distinct.size < values.filter(Boolean).length) return {
|
|
373
|
+
column,
|
|
374
|
+
name: field.name,
|
|
375
|
+
values
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A dataset's groups as its proposal keeps them: the written field's id, and
|
|
382
|
+
* the value of each record its {@link AiSiteDatasetProposal.recordNames} names.
|
|
383
|
+
*/ function aiSiteDatasetGroupsOf(category, written, designed) {
|
|
384
|
+
var _ref;
|
|
385
|
+
var _written_find, _written_category_column;
|
|
386
|
+
if (!category) return {};
|
|
387
|
+
const field = (_ref = (_written_find = written.find((entry)=>entry.name.trim().toLowerCase() === category.name.trim().toLowerCase())) == null ? void 0 : _written_find.id) != null ? _ref : written.length === designed.fields.length ? (_written_category_column = written[category.column]) == null ? void 0 : _written_category_column.id : undefined;
|
|
388
|
+
if (!field) return {};
|
|
389
|
+
const values = designed.records.flatMap((record, index)=>{
|
|
390
|
+
var _category_values_index;
|
|
391
|
+
return record[0] ? [
|
|
392
|
+
(_category_values_index = category.values[index]) != null ? _category_values_index : ''
|
|
393
|
+
] : [];
|
|
394
|
+
}).slice(0, 12);
|
|
395
|
+
return {
|
|
396
|
+
groups: {
|
|
397
|
+
field,
|
|
398
|
+
values
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
346
402
|
/**
|
|
347
403
|
* The fields of a designed dataset that its records leave empty on most of
|
|
348
404
|
* them — every record, or more than half — by the id the data plugin gave
|
|
@@ -371,7 +427,7 @@ const limitReview = (message)=>({
|
|
|
371
427
|
function proposalOf(output) {
|
|
372
428
|
var _output_proposal;
|
|
373
429
|
const raw = (_output_proposal = output.proposal) != null ? _output_proposal : {};
|
|
374
|
-
return {
|
|
430
|
+
return _extends({
|
|
375
431
|
fields: Array.isArray(raw.fields) ? raw.fields.filter((field)=>field && typeof field.id === 'string' && field.id).map((field)=>({
|
|
376
432
|
id: field.id,
|
|
377
433
|
name: str(field.name) || field.id,
|
|
@@ -382,7 +438,12 @@ function proposalOf(output) {
|
|
|
382
438
|
imageField: str(raw.imageField) || null,
|
|
383
439
|
photos: Array.isArray(raw.photos) ? raw.photos.map(str).filter((src)=>src.startsWith('media:')).slice(0, 24) : [],
|
|
384
440
|
sparseFields: Array.isArray(raw.sparseFields) ? raw.sparseFields.map(str).filter(Boolean) : []
|
|
385
|
-
}
|
|
441
|
+
}, raw.groups && typeof raw.groups === 'object' && str(raw.groups.field) && Array.isArray(raw.groups.values) ? {
|
|
442
|
+
groups: {
|
|
443
|
+
field: str(raw.groups.field),
|
|
444
|
+
values: raw.groups.values.slice(0, 12).map(str)
|
|
445
|
+
}
|
|
446
|
+
} : {});
|
|
386
447
|
}
|
|
387
448
|
/**
|
|
388
449
|
* The dataset unit's runner, on the job the scaffold derived for it: the one
|
|
@@ -395,7 +456,7 @@ function proposalOf(output) {
|
|
|
395
456
|
const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : pluginResourceDraftWriter;
|
|
396
457
|
const photosFor = (_deps_photos = deps.photos) != null ? _deps_photos : aiSiteDatasetRecordPhotos;
|
|
397
458
|
return async (context)=>{
|
|
398
|
-
var _ref, _context_org, _facts_fields, _facts_addressField, _facts_fields1, _facts_records;
|
|
459
|
+
var _ref, _context_org, _facts_fields, _facts_addressField, _facts_fields1, _facts_fields2, _facts_records;
|
|
399
460
|
var _job_plan_create, _job_plan;
|
|
400
461
|
const { job, firestore, signal } = context;
|
|
401
462
|
const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, AI_DATASET_STEP)) != null ? _ref : aiModelForStep(AI_DATASET_STEP);
|
|
@@ -429,10 +490,10 @@ function proposalOf(output) {
|
|
|
429
490
|
id: job.$id
|
|
430
491
|
});
|
|
431
492
|
if (written) {
|
|
432
|
-
var
|
|
493
|
+
var _facts_fields3, _facts_addressField1, _facts_imageField;
|
|
433
494
|
const facts = written.facts;
|
|
434
495
|
const again = output(written.id, written.name || creation.name, {
|
|
435
|
-
fields: (
|
|
496
|
+
fields: (_facts_fields3 = facts.fields) != null ? _facts_fields3 : [],
|
|
436
497
|
recordNames: [],
|
|
437
498
|
addressField: (_facts_addressField1 = facts.addressField) != null ? _facts_addressField1 : null,
|
|
438
499
|
imageField: (_facts_imageField = facts.imageField) != null ? _facts_imageField : null
|
|
@@ -448,7 +509,7 @@ function proposalOf(output) {
|
|
|
448
509
|
// A form's dataset (AGL-3616): its records are the submissions, so it is
|
|
449
510
|
// designed from the plan with no model, and starts empty.
|
|
450
511
|
if (input.forForm) {
|
|
451
|
-
var _context_org1,
|
|
512
|
+
var _context_org1, _facts_fields4;
|
|
452
513
|
const content = aiSiteFormDatasetContent(creation.name, creation.fields, input.forForm);
|
|
453
514
|
const checked = keeper.writer.check(content, {
|
|
454
515
|
hostId: job.hostId
|
|
@@ -485,7 +546,7 @@ function proposalOf(output) {
|
|
|
485
546
|
return aiUnspentOutcome(model, {
|
|
486
547
|
outputs: [
|
|
487
548
|
_extends({}, output(result.id, result.name, {
|
|
488
|
-
fields: (
|
|
549
|
+
fields: (_facts_fields4 = facts.fields) != null ? _facts_fields4 : [],
|
|
489
550
|
recordNames: [],
|
|
490
551
|
addressField: null
|
|
491
552
|
}, 0), {
|
|
@@ -517,6 +578,8 @@ function proposalOf(output) {
|
|
|
517
578
|
const designed = aiDatasetDraftContent(creation.name, generation.value, {
|
|
518
579
|
recordPages: input.recordPages
|
|
519
580
|
});
|
|
581
|
+
// What each record is filed under, which its photo and the sections that list it by its value read (AGL-3856).
|
|
582
|
+
const category = aiSiteDatasetCategoryOf(generation.value);
|
|
520
583
|
// A list a visitor looks at keeps its records' photos (AGL-3616): searched
|
|
521
584
|
// in code, never asked of the model, so they cost no credits.
|
|
522
585
|
const photos = input.pictures ? await photosFor(_extends({
|
|
@@ -525,7 +588,10 @@ function proposalOf(output) {
|
|
|
525
588
|
names: generation.value.records.map((values)=>{
|
|
526
589
|
var _values_;
|
|
527
590
|
return (_values_ = values[0]) != null ? _values_ : '';
|
|
528
|
-
})
|
|
591
|
+
})
|
|
592
|
+
}, category ? {
|
|
593
|
+
categories: category.values
|
|
594
|
+
} : {}, {
|
|
529
595
|
pictures: input.pictures
|
|
530
596
|
}, signal ? {
|
|
531
597
|
signal
|
|
@@ -590,7 +656,7 @@ function proposalOf(output) {
|
|
|
590
656
|
photos: placed
|
|
591
657
|
} : {}, {
|
|
592
658
|
sparseFields: aiDatasetSparseFields(generation.value, (_facts_fields1 = facts.fields) != null ? _facts_fields1 : [])
|
|
593
|
-
});
|
|
659
|
+
}, aiSiteDatasetGroupsOf(category, (_facts_fields2 = facts.fields) != null ? _facts_fields2 : [], generation.value));
|
|
594
660
|
return _extends({}, spent, {
|
|
595
661
|
outputs: [
|
|
596
662
|
output(result.id, result.name, proposal, (_facts_records = facts.records) != null ? _facts_records : generation.value.records.length)
|
|
@@ -727,6 +793,201 @@ export const runAiSiteDatasetUnit = createAiSiteDatasetRunner();
|
|
|
727
793
|
});
|
|
728
794
|
return aiDatasetListingsOf(datasets, input.screens);
|
|
729
795
|
}
|
|
796
|
+
/**
|
|
797
|
+
* A SITE START NEVER SHOWS ANOTHER SITE'S RECORDS (AGL-3912).
|
|
798
|
+
*
|
|
799
|
+
* Datasets belong to the organization, and so do their records: neither
|
|
800
|
+
* names the site it was written for, and a dataset outlives the site that
|
|
801
|
+
* filled it (the data plugin keeps `visibleTo` and nothing else of a site). A
|
|
802
|
+
* gym's start reused the workspace's "Session packages", "Coaches" and "Gym
|
|
803
|
+
* FAQs", and published a deleted photographer's wedding packages and a
|
|
804
|
+
* deleted studio's coach bio ("Leads coaching at Ironline Strength Studio")
|
|
805
|
+
* with four broken photos; a photographer's start reused another
|
|
806
|
+
* photographer's gallery and drew six broken covers.
|
|
807
|
+
*
|
|
808
|
+
* So a site start reuses a dataset only when its records are this site's to
|
|
809
|
+
* show ({@link aiSiteDatasetIsThisSites}):
|
|
810
|
+
* - it holds no records at all — an empty list, such as the one a form
|
|
811
|
+
* writes its submissions to, is the same list on any site; or
|
|
812
|
+
* - its scope names this site alone (`siteOnly`), so it was made for it; or
|
|
813
|
+
* - this site already serves its records pages of their own (`recordBase`).
|
|
814
|
+
* Anything else — records in a dataset shared with the workspace or with
|
|
815
|
+
* other sites, and a dataset whose records could not be counted — was filled
|
|
816
|
+
* for something else, and the plan is settled in code before the rules read
|
|
817
|
+
* it ({@link aiSettleSiteForeignDatasets}): the reuse becomes a creation of
|
|
818
|
+
* the same name and fields, designed from this site's brief with photos of
|
|
819
|
+
* its own, and every section, record template and form that named it names
|
|
820
|
+
* the creation. Where this job may create no dataset, the reuse comes off
|
|
821
|
+
* instead and each section writes its items out. Never a refusal, and
|
|
822
|
+
* nothing a member is asked.
|
|
823
|
+
*
|
|
824
|
+
* An Assist change to a site that exists keeps reusing what the site lists:
|
|
825
|
+
* this is a site start's rule, and only its plan is settled by it.
|
|
826
|
+
*/ export function aiSiteDatasetIsThisSites(row) {
|
|
827
|
+
return row.records === 0 || row.siteOnly === true || Boolean(row.recordBase);
|
|
828
|
+
}
|
|
829
|
+
/** A planned field that is a record's page address, which the writer adds itself. */ const ADDRESS_FIELD_NAME = /^(?:page )?(?:address|slug|url|link)$/i;
|
|
830
|
+
/** The fields a dataset is made again with: its own, less the photo, its description and the page address the writer adds. */ function remadeFields(row) {
|
|
831
|
+
var _row_fieldIds;
|
|
832
|
+
const ids = (_row_fieldIds = row.fieldIds) != null ? _row_fieldIds : [];
|
|
833
|
+
return row.fields.filter((name, index)=>{
|
|
834
|
+
const id = ids[index];
|
|
835
|
+
if (id && (id === row.imageField || id === row.addressField)) return false;
|
|
836
|
+
if (IMAGE_FIELD_NAME.test(name.trim()) || ADDRESS_FIELD_NAME.test(name.trim())) return false;
|
|
837
|
+
return name.trim().toLowerCase() !== AI_SITE_DATASET_IMAGE_ALT_FIELD.toLowerCase();
|
|
838
|
+
}).slice(0, AI_BUILD_PLAN_LIMITS.fields);
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* A site start's plan with every dataset it reuses that holds another
|
|
842
|
+
* site's records made again for this one (AGL-3912): each the inventory marks
|
|
843
|
+
* `foreign` ({@link aiSiteInventoryWithRecordCounts}, by
|
|
844
|
+
* {@link aiSiteDatasetIsThisSites}). The creation keeps the dataset's name,
|
|
845
|
+
* which rule 7 does not hold against it as a duplicate. Pure; a plan that
|
|
846
|
+
* reuses none comes back as it was.
|
|
847
|
+
*/ export function aiSettleSiteForeignDatasets(plan, inventory, capabilities = null) {
|
|
848
|
+
var _ref, _plan_items;
|
|
849
|
+
const rows = new Map(((_ref = inventory == null ? void 0 : inventory.datasets) != null ? _ref : []).map((row)=>[
|
|
850
|
+
row.id,
|
|
851
|
+
row
|
|
852
|
+
]));
|
|
853
|
+
const foreign = plan.reuse.filter((entry)=>{
|
|
854
|
+
var _rows_get;
|
|
855
|
+
return entry.kind === 'dataset' && ((_rows_get = rows.get(entry.id)) == null ? void 0 : _rows_get.foreign) === true;
|
|
856
|
+
});
|
|
857
|
+
if (!foreign.length) return plan;
|
|
858
|
+
const creatable = !capabilities || capabilities.create.dataset.allowed;
|
|
859
|
+
const taken = new Map([
|
|
860
|
+
...plan.create.map((entry)=>[
|
|
861
|
+
entry.name.trim().toLowerCase(),
|
|
862
|
+
entry
|
|
863
|
+
]),
|
|
864
|
+
...((_plan_items = plan.items) != null ? _plan_items : []).map((item)=>[
|
|
865
|
+
item.name.trim().toLowerCase(),
|
|
866
|
+
null
|
|
867
|
+
])
|
|
868
|
+
]);
|
|
869
|
+
// Each foreign dataset's id → the `new:` reference that replaces it, or `null` where it comes off.
|
|
870
|
+
const replaced = new Map();
|
|
871
|
+
const added = [];
|
|
872
|
+
for (const entry of foreign){
|
|
873
|
+
const row = rows.get(entry.id);
|
|
874
|
+
const base = row.name.trim() || 'Records';
|
|
875
|
+
if (!creatable) {
|
|
876
|
+
replaced.set(entry.id, null);
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
// The plan already makes one of that name: that one is this site's.
|
|
880
|
+
const same = taken.get(base.toLowerCase());
|
|
881
|
+
if ((same == null ? void 0 : same.kind) === 'dataset') {
|
|
882
|
+
replaced.set(entry.id, `${AI_PLAN_NEW_REF_PREFIX}${same.name}`);
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
let name = base;
|
|
886
|
+
for(let n = 2; taken.has(name.toLowerCase()); n += 1)name = `${base} ${n}`;
|
|
887
|
+
const creation = {
|
|
888
|
+
kind: 'dataset',
|
|
889
|
+
name,
|
|
890
|
+
why: entry.purpose || `Lists ${base} on this site.`,
|
|
891
|
+
duplicateOf: null,
|
|
892
|
+
fields: remadeFields(row)
|
|
893
|
+
};
|
|
894
|
+
added.push(creation);
|
|
895
|
+
taken.set(name.toLowerCase(), creation);
|
|
896
|
+
replaced.set(entry.id, `${AI_PLAN_NEW_REF_PREFIX}${name}`);
|
|
897
|
+
}
|
|
898
|
+
const swap = (ref)=>{
|
|
899
|
+
var _replaced_get;
|
|
900
|
+
return replaced.has(ref) ? (_replaced_get = replaced.get(ref)) != null ? _replaced_get : null : ref;
|
|
901
|
+
};
|
|
902
|
+
return _extends({}, plan, {
|
|
903
|
+
reuse: plan.reuse.filter((entry)=>!(entry.kind === 'dataset' && replaced.has(entry.id))),
|
|
904
|
+
create: [
|
|
905
|
+
...plan.create.map((entry)=>{
|
|
906
|
+
if (entry.kind !== 'form' || !entry.writesTo || !replaced.has(entry.writesTo)) return entry;
|
|
907
|
+
const writesTo = swap(entry.writesTo);
|
|
908
|
+
if (writesTo) return _extends({}, entry, {
|
|
909
|
+
writesTo
|
|
910
|
+
});
|
|
911
|
+
// A form whose dataset comes off sends its submissions to the Inbox only.
|
|
912
|
+
const rest = _extends({}, entry);
|
|
913
|
+
delete rest.writesTo;
|
|
914
|
+
return rest;
|
|
915
|
+
}),
|
|
916
|
+
...added
|
|
917
|
+
],
|
|
918
|
+
screens: plan.screens.map((screen)=>{
|
|
919
|
+
// A record template of a dataset made again shows the new one; of one that comes off, it is a page of its own.
|
|
920
|
+
const record = screen.record && replaced.has(screen.record.dataset) ? {
|
|
921
|
+
record: swap(screen.record.dataset)
|
|
922
|
+
} : null;
|
|
923
|
+
const sections = screen.sections.map((section)=>{
|
|
924
|
+
if (!section.uses.some((ref)=>replaced.has(ref))) return section;
|
|
925
|
+
const uses = section.uses.flatMap((ref)=>{
|
|
926
|
+
const next = swap(ref);
|
|
927
|
+
return next ? [
|
|
928
|
+
next
|
|
929
|
+
] : [];
|
|
930
|
+
});
|
|
931
|
+
// A list that comes off is written out on the page, as fewer items than a dataset binds.
|
|
932
|
+
const dropped = section.uses.some((ref)=>replaced.has(ref) && !replaced.get(ref));
|
|
933
|
+
return _extends({}, section, {
|
|
934
|
+
uses
|
|
935
|
+
}, dropped ? {
|
|
936
|
+
items: Math.min(section.items, AI_TYPED_LIST_MIN_ITEMS - 1)
|
|
937
|
+
} : {});
|
|
938
|
+
});
|
|
939
|
+
return _extends({}, screen, record && screen.record ? {
|
|
940
|
+
record: record.record ? _extends({}, screen.record, {
|
|
941
|
+
dataset: record.record
|
|
942
|
+
}) : null
|
|
943
|
+
} : {}, {
|
|
944
|
+
sections
|
|
945
|
+
});
|
|
946
|
+
})
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
/** The production counter: the data plugin's `dataset` index, read one dataset at a time. */ export const aiDatasetRecordCount = async ({ orgId, id })=>{
|
|
950
|
+
var _pluginRecordIndex;
|
|
951
|
+
const index = (_pluginRecordIndex = pluginRecordIndex('dataset')) == null ? void 0 : _pluginRecordIndex.index;
|
|
952
|
+
if (!index) return null;
|
|
953
|
+
const found = await index.get({
|
|
954
|
+
orgId,
|
|
955
|
+
id
|
|
956
|
+
});
|
|
957
|
+
const records = found == null ? void 0 : found.facts['records'];
|
|
958
|
+
return typeof records === 'number' && Number.isFinite(records) ? records : null;
|
|
959
|
+
};
|
|
960
|
+
/**
|
|
961
|
+
* A site start's inventory with how many records each dataset holds
|
|
962
|
+
* (AGL-3912), and each whose records are not this site's to show
|
|
963
|
+
* ({@link aiSiteDatasetIsThisSites}) marked `foreign`. The count is asked
|
|
964
|
+
* once per plan, of every dataset the site sees, all at once; a count that
|
|
965
|
+
* fails leaves `records` out, which reads as another site's. Never throws.
|
|
966
|
+
*/ export async function aiSiteInventoryWithRecordCounts(inventory, orgId, count = aiDatasetRecordCount) {
|
|
967
|
+
if (!(inventory == null ? void 0 : inventory.datasets.length)) return inventory;
|
|
968
|
+
const counts = await Promise.all(inventory.datasets.map((row)=>count({
|
|
969
|
+
orgId,
|
|
970
|
+
id: row.id
|
|
971
|
+
}).catch((error)=>{
|
|
972
|
+
console.warn('ai site plan: a dataset’s records could not be counted', {
|
|
973
|
+
orgId,
|
|
974
|
+
id: row.id,
|
|
975
|
+
error: String(error)
|
|
976
|
+
});
|
|
977
|
+
return null;
|
|
978
|
+
})));
|
|
979
|
+
return _extends({}, inventory, {
|
|
980
|
+
datasets: inventory.datasets.map((row, index)=>{
|
|
981
|
+
const records = counts[index];
|
|
982
|
+
const counted = typeof records === 'number' ? _extends({}, row, {
|
|
983
|
+
records
|
|
984
|
+
}) : row;
|
|
985
|
+
return aiSiteDatasetIsThisSites(counted) ? counted : _extends({}, counted, {
|
|
986
|
+
foreign: true
|
|
987
|
+
});
|
|
988
|
+
})
|
|
989
|
+
});
|
|
990
|
+
}
|
|
730
991
|
/**
|
|
731
992
|
* The site's datasets a layout or a page unit is handed (AGL-3616), each a
|
|
732
993
|
* `records` listing with the sections of the plan's pages that name it, once
|
|
@@ -734,7 +995,7 @@ export const runAiSiteDatasetUnit = createAiSiteDatasetRunner();
|
|
|
734
995
|
* nowhere: the pages built without it write the items out.
|
|
735
996
|
*/ export function aiSiteDatasetListings(input) {
|
|
736
997
|
const made = input.datasets.flatMap((dataset)=>{
|
|
737
|
-
var _proposal_imageField;
|
|
998
|
+
var _proposal_imageField, _proposal_groups;
|
|
738
999
|
const id = input.delivered.get(dataset.name.trim().toLowerCase());
|
|
739
1000
|
const output = id ? input.outputs.find((entry)=>entry.resource === 'draft' && entry.draftResource === AI_SITE_DATASET_RESOURCE && entry.id === id) : undefined;
|
|
740
1001
|
if (!id || !output) return [];
|
|
@@ -753,7 +1014,8 @@ export const runAiSiteDatasetUnit = createAiSiteDatasetRunner();
|
|
|
753
1014
|
return field.id !== proposal.addressField && field.id !== proposal.imageField && (index === 0 || !((_proposal_sparseFields = proposal.sparseFields) == null ? void 0 : _proposal_sparseFields.includes(field.id)));
|
|
754
1015
|
}),
|
|
755
1016
|
imageField: (_proposal_imageField = proposal.imageField) != null ? _proposal_imageField : null,
|
|
756
|
-
addressField: proposal.addressField
|
|
1017
|
+
addressField: proposal.addressField,
|
|
1018
|
+
groups: (_proposal_groups = proposal.groups) != null ? _proposal_groups : null
|
|
757
1019
|
}
|
|
758
1020
|
];
|
|
759
1021
|
});
|