@aglyn/plugins-ai 1.0.0-beta.218 → 1.0.0-beta.220
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/README.md +2 -2
- package/package.json +18 -18
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/activity/ai-activity-actions.js +8 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/billing/ai-month-meter.d.ts +51 -0
- package/src/lib/billing/ai-month-meter.js +89 -0
- package/src/lib/billing/ai-month-meter.js.map +1 -0
- package/src/lib/billing/ai-overage-close.d.ts +11 -33
- package/src/lib/billing/ai-overage-close.js +4 -3
- package/src/lib/billing/ai-overage-close.js.map +1 -1
- package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
- package/src/lib/billing/ai-overage-cutover.js +1 -28
- package/src/lib/billing/ai-overage-cutover.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +1 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/billing/ai-overage-standing.js +1 -1
- package/src/lib/billing/ai-overage-standing.js.map +1 -1
- package/src/lib/components/ai-allotment-editor.component.js +1 -1
- package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
- package/src/lib/components/ai-assist-provider.component.js +5 -4
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +63 -0
- package/src/lib/components/ai-automation-zones.js +31 -0
- package/src/lib/components/ai-automation-zones.js.map +1 -0
- package/src/lib/components/ai-brief-dialog.component.js +3 -3
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +6 -3
- package/src/lib/components/ai-describe-button.component.js +4 -1
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-experiment-cards.component.js +1 -1
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +8 -0
- package/src/lib/components/ai-job-plan.component.js +21 -4
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
- package/src/lib/components/ai-permissions-on-host.component.js +12 -3
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
- package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
- package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
- package/src/lib/components/ai-product-import-option.component.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +124 -0
- package/src/lib/components/ai-product-zones.js +32 -0
- package/src/lib/components/ai-product-zones.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
- package/src/lib/components/ai-products-proposals.component.js.map +1 -1
- package/src/lib/components/ai-seo-audit-card.component.js +35 -92
- package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +21 -15
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +2 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -2
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +1 -1
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/constants.d.ts +5 -0
- package/src/lib/constants.js +4 -0
- package/src/lib/constants.js.map +1 -1
- package/src/lib/declarations.js +2 -0
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +24 -10
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
- package/src/lib/insights/ai-dataset-catalog.js +57 -0
- package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
- package/src/lib/insights/ai-figure-readers.d.ts +6 -39
- package/src/lib/insights/ai-figure-readers.js +2 -391
- package/src/lib/insights/ai-figure-readers.js.map +1 -1
- package/src/lib/insights/ai-insight-digest.d.ts +10 -1
- package/src/lib/insights/ai-insight-digest.js +16 -4
- package/src/lib/insights/ai-insight-digest.js.map +1 -1
- package/src/lib/insights/ai-insight-readers.d.ts +1 -1
- package/src/lib/insights/ai-insight-readers.js.map +1 -1
- package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
- package/src/lib/jobs/ai-email-bindings.js +75 -42
- package/src/lib/jobs/ai-email-bindings.js.map +1 -1
- package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
- package/src/lib/jobs/ai-job-campaign-step.js +5 -10
- package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
- package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
- package/src/lib/jobs/ai-job-component-step.js +32 -9
- package/src/lib/jobs/ai-job-component-step.js.map +1 -1
- package/src/lib/jobs/ai-job-draft-ids.js +11 -0
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.js +1 -1
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-email-step.d.ts +5 -3
- package/src/lib/jobs/ai-job-email-step.js +11 -4
- package/src/lib/jobs/ai-job-email-step.js.map +1 -1
- package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js +1 -1
- package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
- package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
- package/src/lib/jobs/ai-job-form-step.js +60 -8
- package/src/lib/jobs/ai-job-form-step.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +22 -1
- package/src/lib/jobs/ai-job-generation.js +63 -3
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +1 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
- package/src/lib/jobs/ai-job-layout-step.js +46 -13
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
- package/src/lib/jobs/ai-job-page-sections.js +7 -4
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +7 -2
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
- package/src/lib/jobs/ai-job-plan-conformance.js +40 -1
- package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
- package/src/lib/jobs/ai-job-plan-creations.js +199 -0
- package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +11 -2
- package/src/lib/jobs/ai-job-plan-step.js +49 -23
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
- package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
- package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-products-step.js +54 -32
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
- package/src/lib/jobs/ai-job-seo-step.js +35 -108
- package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
- package/src/lib/jobs/ai-job-site-step.js +6 -6
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
- package/src/lib/jobs/ai-job-template-step.js +73 -15
- package/src/lib/jobs/ai-job-template-step.js.map +1 -1
- package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
- package/src/lib/jobs/ai-job-theme-step.js +1 -1
- package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +17 -9
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.js +1 -1
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +20 -11
- package/src/lib/jobs/ai-workflow-records.js +80 -43
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-allotments.d.ts +27 -6
- package/src/lib/model/ai-allotments.js +54 -14
- package/src/lib/model/ai-allotments.js.map +1 -1
- package/src/lib/model/ai-automation-outline.d.ts +21 -2
- package/src/lib/model/ai-automation-outline.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +57 -0
- package/src/lib/model/ai-build-plan.js +116 -7
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-job-creations.d.ts +55 -0
- package/src/lib/model/ai-job-creations.js +85 -0
- package/src/lib/model/ai-job-creations.js.map +1 -0
- package/src/lib/model/ai-page-job.d.ts +3 -2
- package/src/lib/model/ai-page-job.js +4 -3
- package/src/lib/model/ai-page-job.js.map +1 -1
- package/src/lib/model/ai-seo.d.ts +11 -51
- package/src/lib/model/ai-seo.js +0 -17
- package/src/lib/model/ai-seo.js.map +1 -1
- package/src/lib/model/ai-site-inventory.js +1 -1
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +4 -2
- package/src/lib/model/ai-site-job.js +8 -4
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-template-subjects.d.ts +10 -0
- package/src/lib/model/ai-template-subjects.js +16 -2
- package/src/lib/model/ai-template-subjects.js.map +1 -1
- package/src/lib/model/ai-theme-proposal.d.ts +1 -1
- package/src/lib/model/ai-theme-proposal.js +1 -1
- package/src/lib/model/ai-theme-proposal.js.map +1 -1
- package/src/lib/model/ai-tokens.js +1 -1
- package/src/lib/model/ai-tokens.js.map +1 -1
- package/src/lib/model/ai-usage-by-user.js +1 -1
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +1 -3
- package/src/lib/model/ai-workflow-job.js +1 -2
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +149 -0
- package/src/lib/plan-entitlements.js +168 -0
- package/src/lib/plan-entitlements.js.map +1 -0
- package/src/lib/plugin.js +1 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.js +1 -1
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/model-choice.js +1 -1
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/runtime/ai-device-audit.d.ts +1 -1
- package/src/lib/runtime/ai-device-audit.js +1 -1
- package/src/lib/runtime/ai-device-audit.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
- package/src/lib/runtime/ai-doctrine-validators.js +178 -21
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +5 -5
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +30 -15
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +9 -2
- package/src/lib/runtime/ai-eval.js +29 -2
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-gate.js +1 -1
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.js +3 -3
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +7 -1
- package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
- package/src/lib/runtime/ai-palette.generated.js +16 -13
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js +7 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/seo-audit.d.ts +13 -63
- package/src/lib/runtime/seo-audit.js +22 -207
- package/src/lib/runtime/seo-audit.js.map +1 -1
- package/src/lib/runtime/site-inventory.js +29 -23
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-org.js +1 -1
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js +1 -1
- package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +1 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-allotments.js +1 -1
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-assist-prompts.js +3 -3
- package/src/lib/server/ai-assist-prompts.js.map +1 -1
- package/src/lib/server/ai-assist.js +2 -2
- package/src/lib/server/ai-assist.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +1 -1
- package/src/lib/server/assist-chat.js +13 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-view-context.js +23 -23
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server/billing-credits.d.ts +5 -0
- package/src/lib/server/billing-credits.js +8 -2
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +2 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.d.ts +1 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
- package/src/lib/testing/stand-in-automation-index.js +102 -0
- package/src/lib/testing/stand-in-automation-index.js.map +1 -0
- package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
- package/src/lib/testing/stand-in-dataset-index.js +80 -0
- package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
- package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
- package/src/lib/testing/stand-in-list-send-time.js +77 -0
- package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
- package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
- package/src/lib/testing/stand-in-pipeline-index.js +59 -0
- package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
- package/src/lib/testing/stand-in-product-index.d.ts +20 -0
- package/src/lib/testing/stand-in-product-index.js +105 -0
- package/src/lib/testing/stand-in-product-index.js.map +1 -0
- package/src/lib/tools/ai-component-tool.js +2 -2
- package/src/lib/tools/ai-component-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/tools/ai-seo-tool.d.ts +6 -18
- package/src/lib/tools/ai-seo-tool.js +7 -38
- package/src/lib/tools/ai-seo-tool.js.map +1 -1
- package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
- package/src/lib/tools/ai-workflow-tool.js +1 -1
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
- package/src/lib/usage/ai-allotment-alerts.js +39 -15
- package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
- package/src/lib/usage/ai-quota-checks.d.ts +37 -0
- package/src/lib/usage/ai-quota-checks.js +69 -0
- package/src/lib/usage/ai-quota-checks.js.map +1 -0
- package/src/lib/usage/ai-usage-alerts.js +22 -10
- package/src/lib/usage/ai-usage-alerts.js.map +1 -1
- package/src/lib/usage/ai-usage-by-user.js +1 -1
- package/src/lib/usage/ai-usage-by-user.js.map +1 -1
- package/src/lib/usage/assist-ceiling.d.ts +2 -2
- package/src/lib/usage/assist-ceiling.js +4 -4
- package/src/lib/usage/assist-ceiling.js.map +1 -1
- package/src/lib/usage/assist-credits.d.ts +494 -0
- package/src/lib/usage/assist-credits.js +524 -0
- package/src/lib/usage/assist-credits.js.map +1 -0
- package/src/lib/usage/assist-free-taste.d.ts +1 -1
- package/src/lib/usage/assist-free-taste.js +3 -2
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-signal-mining.js +1 -1
- package/src/lib/usage/assist-signal-mining.js.map +1 -1
- package/src/lib/usage/assist-spend-guards.d.ts +153 -0
- package/src/lib/usage/assist-spend-guards.js +182 -0
- package/src/lib/usage/assist-spend-guards.js.map +1 -0
- package/src/lib/usage/assist-usage.d.ts +1 -1
- package/src/lib/usage/assist-usage.js +1 -1
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.js +1 -1
- package/src/lib/usage/staff-org-ai.js.map +1 -1
- package/src/lib/usage-axes.d.ts +49 -0
- package/src/lib/usage-axes.js +108 -0
- package/src/lib/usage-axes.js.map +1 -0
- package/src/lib/runtime/seo-page-facts.d.ts +0 -61
- package/src/lib/runtime/seo-page-facts.js +0 -150
- package/src/lib/runtime/seo-page-facts.js.map +0 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import { type AiBuildPlan } from '../model/ai-build-plan';
|
|
17
|
+
import { type AiBuildPlan, type AiBuildPlanEmbed } from '../model/ai-build-plan';
|
|
18
18
|
import { type AiPlanCapabilities } from '../model/ai-plan-capabilities';
|
|
19
19
|
import type { AiSiteInventory } from '../model/ai-site-inventory';
|
|
20
20
|
import { type AiNodeTreeContext, type AiNodeTreeResult } from './ai-node-tree';
|
|
@@ -100,8 +100,12 @@ export interface AiDoctrineTreeContext extends AiNodeTreeContext {
|
|
|
100
100
|
} | null;
|
|
101
101
|
/** Placed library assets by media id. */
|
|
102
102
|
assets?: Record<string, AiAssetFacts>;
|
|
103
|
-
/**
|
|
104
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The third-party players the confirmed plan lists for this output
|
|
105
|
+
* (AGL-3433): a Video embed is admitted only where one is listed, playing
|
|
106
|
+
* the link the brief gave or none. Absent or empty admits none.
|
|
107
|
+
*/
|
|
108
|
+
plannedEmbeds?: readonly Pick<AiBuildPlanEmbed, 'host' | 'url'>[];
|
|
105
109
|
framing?: AiCopyFraming;
|
|
106
110
|
/**
|
|
107
111
|
* Whether the workspace keeps reusable components and saved forms
|
|
@@ -495,7 +499,8 @@ export declare function detectCutLines(repairs: readonly string[], written: Read
|
|
|
495
499
|
* wraps one other container and adds nothing, no empty containers, no inline
|
|
496
500
|
* style repeated where a token or a component prop would carry it, no font
|
|
497
501
|
* beyond the theme's, images lazy below the first, video by poster rather
|
|
498
|
-
* than autoplay,
|
|
502
|
+
* than autoplay, no third-party player the confirmed plan does not list, and
|
|
503
|
+
* none playing a video the brief did not give.
|
|
499
504
|
*/
|
|
500
505
|
export declare function detectHeavyDocument(tree: AiDoctrineTree, outputKind: AiOutputKind, context?: AiDoctrineTreeContext): AiDoctrineViolation[];
|
|
501
506
|
/**
|
|
@@ -733,6 +738,22 @@ export declare function detectPlanOverFreeWall(plan: AiBuildPlan, inventory: AiS
|
|
|
733
738
|
* instead of reporting itself done.
|
|
734
739
|
*/
|
|
735
740
|
export declare function detectPlanUnreadableRegions(plan: AiBuildPlan): AiDoctrineViolation[];
|
|
741
|
+
/**
|
|
742
|
+
* Rule 16 (plan): a third-party player is planned where it can play
|
|
743
|
+
* (AGL-3433). A screen of the plan or a component it creates carries at most
|
|
744
|
+
* its kind's budget. A template renders for every record it serves and a
|
|
745
|
+
* layout around every page, so neither carries one, and a form, an email or a
|
|
746
|
+
* dataset has nowhere to. A link the plan gives is one its host plays.
|
|
747
|
+
*/
|
|
748
|
+
export declare function detectPlanEmbeds(plan: AiBuildPlan): AiDoctrineViolation[];
|
|
749
|
+
/**
|
|
750
|
+
* Rule 16 (plan, against the brief): a player is planned only where the brief
|
|
751
|
+
* asks for one (AGL-3433). The plan quotes the brief's words, and those words
|
|
752
|
+
* are found in the brief and ask for a video; a link it gives is one the brief
|
|
753
|
+
* gives. The plan step holds this, being the one door that reads the brief
|
|
754
|
+
* beside the plan.
|
|
755
|
+
*/
|
|
756
|
+
export declare function aiPlanEmbedBriefViolations(plan: AiBuildPlan, brief: string): AiDoctrineViolation[];
|
|
736
757
|
/**
|
|
737
758
|
* Every plan rule, against the site inventory the plan was made from and,
|
|
738
759
|
* where the job read them, what it may create there (AGL-3030). `null`
|
|
@@ -17,11 +17,12 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
17
17
|
*/ import { sanitizeAuthorHtml } from "@aglyn/aglyn/app-utils/author-html";
|
|
18
18
|
import { formatBytes, measureNodeMap, nodeMapBytes } from "@aglyn/aglyn/app-utils/measure-node-map";
|
|
19
19
|
import { MEDIA_CDN_VARIANT_WIDTHS, parseMediaRef } from "@aglyn/aglyn/app-utils/media-ref";
|
|
20
|
-
import { ESTIMATED_PAGE_TRANSFER_BYTES
|
|
20
|
+
import { ESTIMATED_PAGE_TRANSFER_BYTES } from "@aglyn/aglyn/app-utils/plan-entitlements";
|
|
21
|
+
import { FREE_AI_TASTE_CREDITS_PER_MONTH } from "../plan-entitlements.js";
|
|
21
22
|
import { REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY } from "@aglyn/aglyn/app-utils/reusable-component-keys";
|
|
22
23
|
import { parseBreakpointSpan } from "@aglyn/shared-data-enums/breakpoint-span";
|
|
23
24
|
import { renderEmailHtml } from "@aglyn/shared-util-email/email-render";
|
|
24
|
-
import { aiPlanCreateFor, aiPlanUndeclaredRefs, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
25
|
+
import { AI_BUILD_PLAN_CREATION_NOUNS, AI_BUILD_PLAN_EMBED_HOST_NAMES, AI_PLAN_ASKS_FOR_VIDEO, aiEmbedHostOf, aiEmbedVideoKey, aiPlanCreateFor, aiPlanSlugKey, aiPlanUndeclaredRefs, isAiPlanNewRef } from "../model/ai-build-plan.js";
|
|
25
26
|
import { aiCreationNoun, aiPlanUncreatable, aiPlanUncreatableKind } from "../model/ai-plan-capabilities.js";
|
|
26
27
|
import { AI_INSTANCE_REF_PROP, validateAiNodeTree } from "./ai-node-tree.js";
|
|
27
28
|
import { AI_PAGE_SCROLL_TO_PROP, aiPageLinkTarget, aiStoredScrollTargets } from "./ai-page-links.js";
|
|
@@ -1421,7 +1422,7 @@ const GRID = 'muiGrid';
|
|
|
1421
1422
|
{
|
|
1422
1423
|
rule: 10,
|
|
1423
1424
|
code: 'link-unrelated-screen',
|
|
1424
|
-
message: `"${sent[0].label}" links the home page, which does not do what its words say. Link the
|
|
1425
|
+
message: `"${sent[0].label}" links the home page, which does not do what its words say. Link the page that does, or leave the link out when the site has none.`,
|
|
1425
1426
|
nodeIds: unique(sent.map((entry)=>entry.id))
|
|
1426
1427
|
}
|
|
1427
1428
|
];
|
|
@@ -1505,7 +1506,7 @@ const GRID = 'muiGrid';
|
|
|
1505
1506
|
violations.push({
|
|
1506
1507
|
rule: 10,
|
|
1507
1508
|
code: 'link-fragment',
|
|
1508
|
-
message: `${wordsOf(first)} links "${first.value}", an anchor, and no element on a page carries an id an anchor could name, so it goes nowhere. ${context.pageSections ? `${toSection} Otherwise give it the "screenId" of a
|
|
1509
|
+
message: `${wordsOf(first)} links "${first.value}", an anchor, and no element on a page carries an id an anchor could name, so it goes nowhere. ${context.pageSections ? `${toSection} Otherwise give it the "screenId" of a page the site has that does what its words say, or take it out.` : 'Give it the "screenId" of a page the site has that does what its words say, or an "href" that is a path on this site or an https: address the brief gives, or take it out.'}`,
|
|
1509
1510
|
nodeIds: unique(fragments.map((entry)=>entry.id))
|
|
1510
1511
|
});
|
|
1511
1512
|
}
|
|
@@ -1524,7 +1525,7 @@ const GRID = 'muiGrid';
|
|
|
1524
1525
|
violations.push({
|
|
1525
1526
|
rule: 10,
|
|
1526
1527
|
code: 'link-without-destination',
|
|
1527
|
-
message: `${wordsOf(first)} goes nowhere. Give it the "screenId" of a
|
|
1528
|
+
message: `${wordsOf(first)} goes nowhere. Give it the "screenId" of a page the site has that does what its words say, or an "href" that is a path on this site or an https: address the brief gives.${instead}`,
|
|
1528
1529
|
nodeIds: unique(dead.map((entry)=>entry.id))
|
|
1529
1530
|
});
|
|
1530
1531
|
}
|
|
@@ -1884,12 +1885,25 @@ const EMBED_COMPONENTS = new Set([
|
|
|
1884
1885
|
]);
|
|
1885
1886
|
const DUPLICATE_SX_MIN_KEYS = 2;
|
|
1886
1887
|
const DUPLICATE_SX_MIN_NODES = 4;
|
|
1888
|
+
/**
|
|
1889
|
+
* Whether a third-party player is one the confirmed plan lists (AGL-3433). A
|
|
1890
|
+
* Video embed is admitted where the plan lists a player, playing the video
|
|
1891
|
+
* the brief gave or none yet, which the site owner pastes. Raw HTML never is.
|
|
1892
|
+
*/ function embedVerdict(node, planned) {
|
|
1893
|
+
var _node_props;
|
|
1894
|
+
if (node.componentId !== 'videoEmbed' || !planned.length) return 'unplanned';
|
|
1895
|
+
const url = typeof ((_node_props = node.props) == null ? void 0 : _node_props['url']) === 'string' ? node.props['url'].trim() : '';
|
|
1896
|
+
if (!url) return 'planned';
|
|
1897
|
+
const key = aiEmbedVideoKey(url);
|
|
1898
|
+
return key && planned.some((embed)=>embed.url && aiEmbedVideoKey(embed.url) === key) ? 'planned' : 'unplanned-link';
|
|
1899
|
+
}
|
|
1887
1900
|
/**
|
|
1888
1901
|
* Rule 16. The flattest tree that renders the design: no container that
|
|
1889
1902
|
* wraps one other container and adds nothing, no empty containers, no inline
|
|
1890
1903
|
* style repeated where a token or a component prop would carry it, no font
|
|
1891
1904
|
* beyond the theme's, images lazy below the first, video by poster rather
|
|
1892
|
-
* than autoplay,
|
|
1905
|
+
* than autoplay, no third-party player the confirmed plan does not list, and
|
|
1906
|
+
* none playing a video the brief did not give.
|
|
1893
1907
|
*/ export function detectHeavyDocument(tree, outputKind, context = {}) {
|
|
1894
1908
|
const visits = walkTree(tree);
|
|
1895
1909
|
const wrappers = [];
|
|
@@ -1898,6 +1912,7 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
1898
1912
|
const eager = [];
|
|
1899
1913
|
const video = [];
|
|
1900
1914
|
const embeds = [];
|
|
1915
|
+
const embedLinks = [];
|
|
1901
1916
|
const sxSeen = new Map();
|
|
1902
1917
|
let images = 0;
|
|
1903
1918
|
for (const { id, node } of visits){
|
|
@@ -1920,7 +1935,12 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
1920
1935
|
if (node.componentId === 'video' && (((_node_props2 = node.props) == null ? void 0 : _node_props2['autoPlay']) === true || ((_node_props3 = node.props) == null ? void 0 : _node_props3['preload']) === 'auto' || typeof ((_node_props4 = node.props) == null ? void 0 : _node_props4['src']) === 'string' && node.props['src'] && !((_node_props5 = node.props) == null ? void 0 : _node_props5['poster']))) {
|
|
1921
1936
|
video.push(id);
|
|
1922
1937
|
}
|
|
1923
|
-
if (EMBED_COMPONENTS.has(node.componentId)
|
|
1938
|
+
if (EMBED_COMPONENTS.has(node.componentId)) {
|
|
1939
|
+
var _context_plannedEmbeds;
|
|
1940
|
+
const verdict = embedVerdict(node, (_context_plannedEmbeds = context.plannedEmbeds) != null ? _context_plannedEmbeds : []);
|
|
1941
|
+
if (verdict === 'unplanned') embeds.push(id);
|
|
1942
|
+
if (verdict === 'unplanned-link') embedLinks.push(id);
|
|
1943
|
+
}
|
|
1924
1944
|
const sx = (_node_sx1 = node.sx) != null ? _node_sx1 : {};
|
|
1925
1945
|
if (Object.keys(sx).length >= DUPLICATE_SX_MIN_KEYS) {
|
|
1926
1946
|
var _sxSeen_get;
|
|
@@ -1952,7 +1972,8 @@ const DUPLICATE_SX_MIN_NODES = 4;
|
|
|
1952
1972
|
add('extra-font', fonts, "This names a font the theme does not load. Use the theme's typography.");
|
|
1953
1973
|
add('eager-image', eager, 'An image below the first loads eagerly. Leave loading unset so images further down the page load when they are reached.');
|
|
1954
1974
|
add('autoplay-video', video, 'A video loads its file before anyone plays it. Give it a poster, and let it play on click.');
|
|
1955
|
-
add('third-party-embed', embeds,
|
|
1975
|
+
add('third-party-embed', embeds, "This embeds a third-party player, which loads its host's code on every visit, and the confirmed plan lists none here. Remove it; a film from the media library plays in a Video (video).");
|
|
1976
|
+
add('embed-link-unplanned', embedLinks, 'This player plays a video the brief did not give. Use the link the plan lists, or leave the link empty for the site owner to paste.');
|
|
1956
1977
|
return violations;
|
|
1957
1978
|
}
|
|
1958
1979
|
/** The items a list or a row of cards repeats, as a re-ask names them. */ const ITEM_NOUNS = {
|
|
@@ -2513,7 +2534,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2513
2534
|
violations.push({
|
|
2514
2535
|
rule: 1,
|
|
2515
2536
|
code: 'plan-section-across-screens',
|
|
2516
|
-
message: `The "${entries[0].section.name}" section is planned on ${screens.size}
|
|
2537
|
+
message: `The "${entries[0].section.name}" section is planned on ${screens.size} pages. Plan it as one reusable component and place it on each.`,
|
|
2517
2538
|
paths: entries.map((entry)=>entry.path)
|
|
2518
2539
|
});
|
|
2519
2540
|
}
|
|
@@ -2638,7 +2659,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2638
2659
|
violations.push({
|
|
2639
2660
|
rule: 2,
|
|
2640
2661
|
code: 'plan-screen-without-layout',
|
|
2641
|
-
message: noLayoutToName ? 'A
|
|
2662
|
+
message: noLayoutToName ? 'A page names a layout the site does not have, and this job may not create one. Leave the layout empty.' : mayCreateLayout ? "A page names no layout the site has or the plan creates. Put every page in the site's layout, or plan one." : 'A page names no layout the site has, and this job may not create one. Put every page in a layout the site has.',
|
|
2642
2663
|
paths: unlaid.map(({ index })=>`screens[${index}].layout`)
|
|
2643
2664
|
});
|
|
2644
2665
|
}
|
|
@@ -2647,7 +2668,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2647
2668
|
violations.push({
|
|
2648
2669
|
rule: 2,
|
|
2649
2670
|
code: 'plan-layout-region-section',
|
|
2650
|
-
message: 'A
|
|
2671
|
+
message: 'A page plans its own header, navigation or footer. Those live in the layout; take the section off the page.',
|
|
2651
2672
|
paths: regions.map((entry)=>entry.path)
|
|
2652
2673
|
});
|
|
2653
2674
|
}
|
|
@@ -2656,7 +2677,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2656
2677
|
violations.push({
|
|
2657
2678
|
rule: 2,
|
|
2658
2679
|
code: 'plan-layout-in-section',
|
|
2659
|
-
message: "A section places a layout. A layout frames a whole
|
|
2680
|
+
message: "A section places a layout. A layout frames a whole page and is never placed inside one: name it as the page's layout, and take it out of the section's uses.",
|
|
2660
2681
|
paths: placed
|
|
2661
2682
|
});
|
|
2662
2683
|
}
|
|
@@ -2703,7 +2724,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2703
2724
|
violations.push({
|
|
2704
2725
|
rule: 4,
|
|
2705
2726
|
code: 'plan-similar-pages',
|
|
2706
|
-
message: `${indexes.length}
|
|
2727
|
+
message: `${indexes.length} pages share one shape. Plan one template and apply it ${indexes.length} times with each page's copy, or bind it to a collection.`,
|
|
2707
2728
|
paths: indexes.map((index)=>`screens[${index}].template`)
|
|
2708
2729
|
});
|
|
2709
2730
|
}
|
|
@@ -2712,7 +2733,7 @@ const FORM_SECTION_NAME = /\bform\b|\bsign[- ]?up\b|\bsubscribe\b|\bnewsletter\b
|
|
|
2712
2733
|
violations.push({
|
|
2713
2734
|
rule: 4,
|
|
2714
2735
|
code: 'plan-template-in-section',
|
|
2715
|
-
message: "A section places a template. A template is applied to a whole
|
|
2736
|
+
message: "A section places a template. A template is applied to a whole page and is never placed inside one: take it out of the section's uses, and name it as the page's template only when the whole page is built from it.",
|
|
2716
2737
|
paths: placed
|
|
2717
2738
|
});
|
|
2718
2739
|
}
|
|
@@ -2851,15 +2872,15 @@ const REUSE_BEFORE_CREATE_KINDS = new Set([
|
|
|
2851
2872
|
const section = first.field === 'uses' ? screen.sections[first.sectionIndex] : null;
|
|
2852
2873
|
const kind = section ? undeclaredPlacementKind(first.name, section, plan, kinds) : 'template';
|
|
2853
2874
|
const refused = capabilities ? aiPlanUncreatableKind(plan, kind, capabilities) : null;
|
|
2854
|
-
const where = section ? `${section.name ? `The "${section.name}" section` : 'A section'} places a creation named "${first.name}", but the plan never creates it` : `${screen.title ? `The
|
|
2875
|
+
const where = section ? `${section.name ? `The "${section.name}" section` : 'A section'} places a creation named "${first.name}", but the plan never creates it` : `${screen.title ? `The page "${screen.title}"` : 'A page'} applies a template named "${first.name}", but the plan never creates it`;
|
|
2855
2876
|
let message;
|
|
2856
2877
|
if (!refused) {
|
|
2857
|
-
const otherwise = section ? `place ${aiCreationNoun(kind)} the site already has by its id` : "leave the
|
|
2878
|
+
const otherwise = section ? `place ${aiCreationNoun(kind)} the site already has by its id` : "leave the page's template empty";
|
|
2858
2879
|
message = `${where}. Declare it in create as ${aiCreationNoun(kind)}, with why nothing the site has will do, or ${otherwise}.`;
|
|
2859
2880
|
} else if (section) {
|
|
2860
2881
|
message = `${where}, and ${refused.reason}. ${refused.instead} Take it out of the section's uses.`;
|
|
2861
2882
|
} else {
|
|
2862
|
-
message = `${where}, and ${refused.reason}. Leave the
|
|
2883
|
+
message = `${where}, and ${refused.reason}. Leave the page's template empty, or apply a template the site already has.`;
|
|
2863
2884
|
}
|
|
2864
2885
|
return {
|
|
2865
2886
|
rule: 7,
|
|
@@ -2951,7 +2972,7 @@ function slugKey(slug) {
|
|
|
2951
2972
|
violations.push({
|
|
2952
2973
|
rule: 10,
|
|
2953
2974
|
code: 'plan-slug',
|
|
2954
|
-
message: 'A
|
|
2975
|
+
message: 'A page has no usable address. Give each one a slug of lowercase words joined by hyphens.',
|
|
2955
2976
|
paths: badSlugs
|
|
2956
2977
|
});
|
|
2957
2978
|
}
|
|
@@ -2959,7 +2980,7 @@ function slugKey(slug) {
|
|
|
2959
2980
|
violations.push({
|
|
2960
2981
|
rule: 10,
|
|
2961
2982
|
code: 'plan-slug-taken',
|
|
2962
|
-
message: 'A
|
|
2983
|
+
message: 'A page reuses an address the site or the plan already uses. Give each page its own slug.',
|
|
2963
2984
|
paths: collisions
|
|
2964
2985
|
});
|
|
2965
2986
|
}
|
|
@@ -2967,7 +2988,7 @@ function slugKey(slug) {
|
|
|
2967
2988
|
violations.push({
|
|
2968
2989
|
rule: 10,
|
|
2969
2990
|
code: 'plan-seo',
|
|
2970
|
-
message: `A
|
|
2991
|
+
message: `A page is missing its search title or description, or runs past ${AI_SEO_TITLE_MAX} and ${AI_SEO_DESCRIPTION_MAX} characters. Write both for every page.`,
|
|
2971
2992
|
paths: seo
|
|
2972
2993
|
});
|
|
2973
2994
|
}
|
|
@@ -3014,7 +3035,7 @@ function slugKey(slug) {
|
|
|
3014
3035
|
violations.push({
|
|
3015
3036
|
rule: 15,
|
|
3016
3037
|
code: 'plan-missed-duplicate',
|
|
3017
|
-
message: `The site already has "${nearest.name}". Start the new
|
|
3038
|
+
message: `The site already has "${nearest.name}". Start the new page from a duplicate of it, which keeps its bindings and SEO, and edit that.`,
|
|
3018
3039
|
paths: [
|
|
3019
3040
|
`screens[${index}].duplicateOf`
|
|
3020
3041
|
]
|
|
@@ -3155,6 +3176,141 @@ function slugKey(slug) {
|
|
|
3155
3176
|
}
|
|
3156
3177
|
];
|
|
3157
3178
|
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Rule 16 (plan): a third-party player is planned where it can play
|
|
3181
|
+
* (AGL-3433). A screen of the plan or a component it creates carries at most
|
|
3182
|
+
* its kind's budget. A template renders for every record it serves and a
|
|
3183
|
+
* layout around every page, so neither carries one, and a form, an email or a
|
|
3184
|
+
* dataset has nowhere to. A link the plan gives is one its host plays.
|
|
3185
|
+
*/ export function detectPlanEmbeds(plan) {
|
|
3186
|
+
var _plan_embeds;
|
|
3187
|
+
const nowhere = [];
|
|
3188
|
+
const elsewhere = [];
|
|
3189
|
+
const wrongHost = [];
|
|
3190
|
+
const placed = new Map();
|
|
3191
|
+
((_plan_embeds = plan.embeds) != null ? _plan_embeds : []).forEach((embed, index)=>{
|
|
3192
|
+
var _placed_get;
|
|
3193
|
+
const path = `embeds[${index}]`;
|
|
3194
|
+
if (embed.url !== null && aiEmbedHostOf(embed.url) !== embed.host) wrongHost.push(`${path}.url`);
|
|
3195
|
+
let target = null;
|
|
3196
|
+
if (isAiPlanNewRef(embed.where)) {
|
|
3197
|
+
const creation = aiPlanCreateFor(plan, embed.where);
|
|
3198
|
+
if (creation && creation.kind !== 'component') {
|
|
3199
|
+
elsewhere.push({
|
|
3200
|
+
kind: creation.kind,
|
|
3201
|
+
path: `${path}.where`
|
|
3202
|
+
});
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
if (creation) target = {
|
|
3206
|
+
key: embed.where.trim().toLowerCase(),
|
|
3207
|
+
label: creation.name,
|
|
3208
|
+
kind: 'component'
|
|
3209
|
+
};
|
|
3210
|
+
} else {
|
|
3211
|
+
const screen = plan.screens.find((row)=>aiPlanSlugKey(row.slug) === aiPlanSlugKey(embed.where));
|
|
3212
|
+
if (screen) target = {
|
|
3213
|
+
key: aiPlanSlugKey(screen.slug),
|
|
3214
|
+
label: screen.slug,
|
|
3215
|
+
kind: 'page'
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
if (!target) {
|
|
3219
|
+
nowhere.push(`${path}.where`);
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
const entry = (_placed_get = placed.get(target.key)) != null ? _placed_get : {
|
|
3223
|
+
label: target.label,
|
|
3224
|
+
kind: target.kind,
|
|
3225
|
+
paths: []
|
|
3226
|
+
};
|
|
3227
|
+
entry.paths.push(path);
|
|
3228
|
+
placed.set(target.key, entry);
|
|
3229
|
+
});
|
|
3230
|
+
const violations = [];
|
|
3231
|
+
if (nowhere.length) {
|
|
3232
|
+
violations.push({
|
|
3233
|
+
rule: 16,
|
|
3234
|
+
code: 'plan-embed-nowhere',
|
|
3235
|
+
message: 'A player is planned where the plan builds nothing. Name a screen of the plan by its slug, or new:<name> of a component it creates.',
|
|
3236
|
+
paths: nowhere
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
if (elsewhere.length) {
|
|
3240
|
+
const nouns = [
|
|
3241
|
+
...new Set(elsewhere.map((entry)=>AI_BUILD_PLAN_CREATION_NOUNS[entry.kind].noun))
|
|
3242
|
+
];
|
|
3243
|
+
violations.push({
|
|
3244
|
+
rule: 16,
|
|
3245
|
+
code: 'plan-embed-refused',
|
|
3246
|
+
message: `Only a page or a component carries a third-party player, and this plan puts one on a ${nouns.join(' and a ')}. A template or a layout would load the player's code on every page it serves. Place it on a page or a component, or leave it off.`,
|
|
3247
|
+
paths: elsewhere.map((entry)=>entry.path)
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3250
|
+
for (const entry of placed.values()){
|
|
3251
|
+
const limit = AI_OUTPUT_BUDGETS[entry.kind].embeds;
|
|
3252
|
+
if (entry.paths.length <= limit) continue;
|
|
3253
|
+
violations.push({
|
|
3254
|
+
rule: 16,
|
|
3255
|
+
code: 'plan-embed-over-budget',
|
|
3256
|
+
message: `${entry.label} is planned ${entry.paths.length} players, and a ${entry.kind} carries at most ${limit}. Keep the one the brief asks for most.`,
|
|
3257
|
+
paths: entry.paths
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
if (wrongHost.length) {
|
|
3261
|
+
violations.push({
|
|
3262
|
+
rule: 16,
|
|
3263
|
+
code: 'plan-embed-link-host',
|
|
3264
|
+
message: `A planned player's link is not a video link of its host, ${Object.values(AI_BUILD_PLAN_EMBED_HOST_NAMES).join(' or ')}. Give the brief's own link for that host, or null.`,
|
|
3265
|
+
paths: wrongHost
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
return violations;
|
|
3269
|
+
}
|
|
3270
|
+
/** Text as two copies of one phrase compare: lowercase, no quotes, one space. */ function phraseKey(text) {
|
|
3271
|
+
return text.toLowerCase().replace(/[“”"‘’'`]/g, '').replace(/\s+/g, ' ').trim();
|
|
3272
|
+
}
|
|
3273
|
+
/**
|
|
3274
|
+
* Rule 16 (plan, against the brief): a player is planned only where the brief
|
|
3275
|
+
* asks for one (AGL-3433). The plan quotes the brief's words, and those words
|
|
3276
|
+
* are found in the brief and ask for a video; a link it gives is one the brief
|
|
3277
|
+
* gives. The plan step holds this, being the one door that reads the brief
|
|
3278
|
+
* beside the plan.
|
|
3279
|
+
*/ export function aiPlanEmbedBriefViolations(plan, brief) {
|
|
3280
|
+
var _brief_match, _plan_embeds;
|
|
3281
|
+
const said = phraseKey(brief);
|
|
3282
|
+
const given = new Set(((_brief_match = brief.match(/https:\/\/\S+/g)) != null ? _brief_match : []).map((link)=>aiEmbedVideoKey(link.replace(/[).,;!?]+$/, ''))).filter((key)=>key !== null));
|
|
3283
|
+
const unasked = [];
|
|
3284
|
+
const ungiven = [];
|
|
3285
|
+
((_plan_embeds = plan.embeds) != null ? _plan_embeds : []).forEach((embed, index)=>{
|
|
3286
|
+
const asked = phraseKey(embed.asked);
|
|
3287
|
+
if (!asked || !said.includes(asked) || !AI_PLAN_ASKS_FOR_VIDEO.test(asked)) {
|
|
3288
|
+
unasked.push(`embeds[${index}].asked`);
|
|
3289
|
+
}
|
|
3290
|
+
if (embed.url !== null) {
|
|
3291
|
+
const key = aiEmbedVideoKey(embed.url);
|
|
3292
|
+
if (!key || !given.has(key)) ungiven.push(`embeds[${index}].url`);
|
|
3293
|
+
}
|
|
3294
|
+
});
|
|
3295
|
+
const violations = [];
|
|
3296
|
+
if (unasked.length) {
|
|
3297
|
+
violations.push({
|
|
3298
|
+
rule: 16,
|
|
3299
|
+
code: 'plan-embed-not-asked',
|
|
3300
|
+
message: 'A player is planned that the brief does not ask for: its words are not words of the brief asking for a video. Take it off the plan; a film from the media library plays in a Video.',
|
|
3301
|
+
paths: unasked
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3304
|
+
if (ungiven.length) {
|
|
3305
|
+
violations.push({
|
|
3306
|
+
rule: 16,
|
|
3307
|
+
code: 'plan-embed-link-not-given',
|
|
3308
|
+
message: "A planned player's link is not one the brief gives. Give the brief's own link, or null for the site owner to paste.",
|
|
3309
|
+
paths: ungiven
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
return violations;
|
|
3313
|
+
}
|
|
3158
3314
|
/**
|
|
3159
3315
|
* Every plan rule, against the site inventory the plan was made from and,
|
|
3160
3316
|
* where the job read them, what it may create there (AGL-3030). `null`
|
|
@@ -3169,6 +3325,7 @@ function slugKey(slug) {
|
|
|
3169
3325
|
...detectPlanInlineForms(plan, inventory, capabilities),
|
|
3170
3326
|
...detectUntemplatedSimilarPages(plan, inventory),
|
|
3171
3327
|
...detectPlanLiteralColors(plan),
|
|
3328
|
+
...detectPlanEmbeds(plan),
|
|
3172
3329
|
...detectCreateBeforeReuse(plan, inventory),
|
|
3173
3330
|
...detectPlanUncreatable(plan, capabilities),
|
|
3174
3331
|
...detectPlanUndeclaredCreations(plan, inventory, capabilities),
|