@aglyn/plugins-ai 1.0.0-beta.229 → 1.0.0-beta.231
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 +3 -3
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +38 -0
- package/src/lib/components/ai-automation-zones.js +5 -1
- package/src/lib/components/ai-automation-zones.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
- package/src/lib/components/ai-brief-dialog.component.js +87 -8
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
- package/src/lib/components/ai-campaign-create.component.js +270 -0
- package/src/lib/components/ai-campaign-create.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
- package/src/lib/components/ai-describe-automation.component.js +15 -6
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +20 -8
- package/src/lib/components/ai-describe-button.component.js +19 -36
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-email.component.d.ts +31 -0
- package/src/lib/components/ai-describe-email.component.js +22 -0
- package/src/lib/components/ai-describe-email.component.js.map +1 -0
- package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
- package/src/lib/components/ai-describe-org-automation.component.js +238 -0
- package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
- package/src/lib/components/ai-experiment-cards.component.js +104 -18
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.js +1 -5
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
- package/src/lib/components/ai-funnel-zones.component.js +178 -0
- package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
- package/src/lib/components/ai-insight-card.component.d.ts +42 -0
- package/src/lib/components/ai-insight-card.component.js +137 -0
- package/src/lib/components/ai-insight-card.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
- package/src/lib/components/ai-insight-dialog.component.js +9 -3
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +7 -2
- package/src/lib/components/ai-job-plan.component.js +38 -6
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +17 -1
- package/src/lib/components/ai-job-requests.js +39 -3
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-logic-zones.d.ts +64 -0
- package/src/lib/components/ai-logic-zones.js +18 -0
- package/src/lib/components/ai-logic-zones.js.map +1 -0
- package/src/lib/components/ai-logic.component.d.ts +77 -0
- package/src/lib/components/ai-logic.component.js +553 -0
- package/src/lib/components/ai-logic.component.js.map +1 -0
- package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
- package/src/lib/components/ai-marketing-insight.component.js +80 -0
- package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
- package/src/lib/components/ai-media-create.component.d.ts +55 -0
- package/src/lib/components/ai-media-create.component.js +433 -0
- package/src/lib/components/ai-media-create.component.js.map +1 -0
- package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
- package/src/lib/components/ai-overlay-cards.component.js +505 -0
- package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-product-zones.d.ts +5 -0
- package/src/lib/components/ai-product-zones.js +1 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
- package/src/lib/components/ai-products-create-button.component.js +67 -0
- package/src/lib/components/ai-products-create-button.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.js +100 -81
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
- package/src/lib/components/ai-revise-automation.component.js +207 -0
- package/src/lib/components/ai-revise-automation.component.js.map +1 -0
- package/src/lib/components/ai-site-build-page.component.js +160 -24
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
- package/src/lib/components/ai-upsell-dialog.component.js +177 -0
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
- package/src/lib/components/assist-build-card.component.d.ts +42 -0
- package/src/lib/components/assist-build-card.component.js +183 -0
- package/src/lib/components/assist-build-card.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +2 -0
- package/src/lib/components/assist-edit-canvas.js +56 -1
- package/src/lib/components/assist-edit-canvas.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.js +48 -3
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +51 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -14
- package/src/lib/components/use-ai-job-run.js +0 -50
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
- package/src/lib/jobs/ai-build-capabilities.js +256 -0
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
- package/src/lib/jobs/ai-build-unit-outcome.js +140 -0
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
- package/src/lib/jobs/ai-job-build-step.js +668 -0
- package/src/lib/jobs/ai-job-build-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -1
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
- package/src/lib/jobs/ai-job-drafts.js +25 -4
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +8 -1
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
- package/src/lib/jobs/ai-job-layout-language.js +194 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +105 -5
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
- package/src/lib/jobs/ai-job-logic-step.js +395 -0
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
- package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
- package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
- package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
- package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
- package/src/lib/jobs/ai-job-page-language.js +355 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +111 -20
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +27 -2
- package/src/lib/jobs/ai-job-plan-step.js +87 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +19 -1
- package/src/lib/jobs/ai-job-site-step.js +220 -61
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
- package/src/lib/jobs/ai-job-text-step.js +12 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
- package/src/lib/jobs/ai-job-workflow-step.js +320 -15
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +80 -2
- package/src/lib/jobs/ai-jobs.js +391 -18
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/jobs/ai-logic-records.d.ts +40 -0
- package/src/lib/jobs/ai-logic-records.js +55 -0
- package/src/lib/jobs/ai-logic-records.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +7 -0
- package/src/lib/jobs/ai-site-publish.js +9 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
- package/src/lib/jobs/ai-workflow-records.js +40 -1
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1148 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +457 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +161 -0
- package/src/lib/layout-language/ai-layout-language.js +446 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +39 -0
- package/src/lib/layout-language/ai-layout-store.js +69 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-automation-draft.d.ts +18 -0
- package/src/lib/model/ai-automation-draft.js +86 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +201 -0
- package/src/lib/model/ai-build-job.js +472 -0
- package/src/lib/model/ai-build-job.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +61 -0
- package/src/lib/model/ai-build-plan.js +154 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +73 -0
- package/src/lib/model/ai-build-progress.js +170 -0
- package/src/lib/model/ai-build-progress.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +20 -10
- package/src/lib/model/ai-insight.js +72 -9
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +25 -2
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +99 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +108 -0
- package/src/lib/model/ai-logic-job.js +327 -0
- package/src/lib/model/ai-logic-job.js.map +1 -0
- package/src/lib/model/ai-media-credits.d.ts +52 -0
- package/src/lib/model/ai-media-credits.js +61 -0
- package/src/lib/model/ai-media-credits.js.map +1 -0
- package/src/lib/model/ai-overlay-copy.d.ts +114 -0
- package/src/lib/model/ai-overlay-copy.js +175 -0
- package/src/lib/model/ai-overlay-copy.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +25 -4
- package/src/lib/model/ai-site-build-progress.js +194 -11
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +8 -0
- package/src/lib/model/ai-site-job.js +9 -0
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +5 -2
- package/src/lib/model/ai-site-start.js +5 -2
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-svg.d.ts +69 -0
- package/src/lib/model/ai-svg.js +265 -0
- package/src/lib/model/ai-svg.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +3 -2
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +92 -4
- package/src/lib/model/ai-workflow-job.js +124 -7
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/model/assist-build.d.ts +58 -0
- package/src/lib/model/assist-build.js +52 -0
- package/src/lib/model/assist-build.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +48 -3
- package/src/lib/model/assist-edit.js +48 -4
- package/src/lib/model/assist-edit.js.map +1 -1
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +308 -47
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +82 -5
- package/src/lib/providers/catalog.js +83 -12
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +116 -0
- package/src/lib/providers/image-contract.js +74 -0
- package/src/lib/providers/image-contract.js.map +1 -0
- package/src/lib/providers/model-choice.js +11 -0
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image.d.ts +121 -0
- package/src/lib/providers/vertex-image.js +312 -0
- package/src/lib/providers/vertex-image.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +101 -0
- package/src/lib/runtime/ai-doctrine-validators.js +525 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +21 -6
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +15 -1
- package/src/lib/runtime/ai-eval.js +52 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
- package/src/lib/runtime/experiment-variant-copy.js +102 -0
- package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
- package/src/lib/server/ai-experiment-versions.d.ts +51 -0
- package/src/lib/server/ai-experiment-versions.js +270 -0
- package/src/lib/server/ai-experiment-versions.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.js +74 -4
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-media-image.d.ts +66 -0
- package/src/lib/server/ai-media-image.js +521 -0
- package/src/lib/server/ai-media-image.js.map +1 -0
- package/src/lib/server/ai-media-svg.d.ts +91 -0
- package/src/lib/server/ai-media-svg.js +186 -0
- package/src/lib/server/ai-media-svg.js.map +1 -0
- package/src/lib/server/assist-build.d.ts +54 -0
- package/src/lib/server/assist-build.js +121 -0
- package/src/lib/server/assist-build.js.map +1 -0
- package/src/lib/server/assist-chat.js +108 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +213 -3
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-view-context.js +242 -4
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +26 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-logic-tool.d.ts +27 -0
- package/src/lib/tools/ai-logic-tool.js +207 -0
- package/src/lib/tools/ai-logic-tool.js.map +1 -0
- package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
- package/src/lib/tools/ai-overlay-tool.js +107 -0
- package/src/lib/tools/ai-overlay-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +17 -2
- package/src/lib/tools/ai-workflow-tool.js +47 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +28 -1
- package/src/lib/usage/assist-job-refund.js +44 -9
- package/src/lib/usage/assist-job-refund.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +5 -0
- package/src/lib/usage/assist-usage.js +2 -0
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* A layout at least as tall as the window (AGL-3596).
|
|
19
|
+
*
|
|
20
|
+
* A layout is header, page, footer, top to bottom, and on a page shorter than
|
|
21
|
+
* the window the footer used to end wherever the page did — on beta.230 a
|
|
22
|
+
* contact page left its footer floating halfway up the screen with blank page
|
|
23
|
+
* under it. So every layout the AI builds is written as one column at least
|
|
24
|
+
* the window's height, and the part of it that holds the Layout Slot takes
|
|
25
|
+
* the room left over: the footer sits at the bottom of a short page and
|
|
26
|
+
* follows the content of a long one.
|
|
27
|
+
*
|
|
28
|
+
* Written by the platform after the model answers and before the doctrine
|
|
29
|
+
* checks the tree, so the doctrine holds the column like anything else: it is
|
|
30
|
+
* a Stack with a style of its own, which no rule calls an empty wrapper.
|
|
31
|
+
*/
|
|
32
|
+
/** The id of the column the platform adds, when no node of the tree has it. */
|
|
33
|
+
export declare const AI_LAYOUT_FRAME_ID = "aiLayoutFrame";
|
|
34
|
+
/**
|
|
35
|
+
* The layout with its root's children in a column at least the window's
|
|
36
|
+
* height, and the child that holds the Layout Slot growing into the room
|
|
37
|
+
* left. A tree whose top already sets a minimum height is left as it came,
|
|
38
|
+
* and so is anything that is not a node map or holds no Layout Slot.
|
|
39
|
+
*/
|
|
40
|
+
export declare function aiLayoutWithFullHeight(tree: unknown): unknown;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /**
|
|
18
|
+
* A layout at least as tall as the window (AGL-3596).
|
|
19
|
+
*
|
|
20
|
+
* A layout is header, page, footer, top to bottom, and on a page shorter than
|
|
21
|
+
* the window the footer used to end wherever the page did — on beta.230 a
|
|
22
|
+
* contact page left its footer floating halfway up the screen with blank page
|
|
23
|
+
* under it. So every layout the AI builds is written as one column at least
|
|
24
|
+
* the window's height, and the part of it that holds the Layout Slot takes
|
|
25
|
+
* the room left over: the footer sits at the bottom of a short page and
|
|
26
|
+
* follows the content of a long one.
|
|
27
|
+
*
|
|
28
|
+
* Written by the platform after the model answers and before the doctrine
|
|
29
|
+
* checks the tree, so the doctrine holds the column like anything else: it is
|
|
30
|
+
* a Stack with a style of its own, which no rule calls an empty wrapper.
|
|
31
|
+
*/ /** The id of the column the platform adds, when no node of the tree has it. */ export const AI_LAYOUT_FRAME_ID = 'aiLayoutFrame';
|
|
32
|
+
const isRecord = (value)=>Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
33
|
+
const childrenOf = (node)=>Array.isArray(node == null ? void 0 : node.nodes) ? node.nodes.filter((id)=>typeof id === 'string') : [];
|
|
34
|
+
/**
|
|
35
|
+
* The layout with its root's children in a column at least the window's
|
|
36
|
+
* height, and the child that holds the Layout Slot growing into the room
|
|
37
|
+
* left. A tree whose top already sets a minimum height is left as it came,
|
|
38
|
+
* and so is anything that is not a node map or holds no Layout Slot.
|
|
39
|
+
*/ export function aiLayoutWithFullHeight(tree) {
|
|
40
|
+
var _source_grower_sx;
|
|
41
|
+
var _source_top__sx;
|
|
42
|
+
if (!isRecord(tree) || !isRecord(tree['nodes'])) return tree;
|
|
43
|
+
const source = tree['nodes'];
|
|
44
|
+
const rootId = typeof tree['rootId'] === 'string' ? tree['rootId'] : null;
|
|
45
|
+
if (!rootId || !isRecord(source[rootId])) return tree;
|
|
46
|
+
const top = childrenOf(source[rootId]).filter((id)=>isRecord(source[id]));
|
|
47
|
+
if (!top.length) return tree;
|
|
48
|
+
if (top.length === 1 && ((_source_top__sx = source[top[0]].sx) == null ? void 0 : _source_top__sx['minHeight']) !== undefined) return tree;
|
|
49
|
+
const holdsSlot = (id, seen = new Set())=>{
|
|
50
|
+
if (seen.has(id) || !isRecord(source[id])) return false;
|
|
51
|
+
seen.add(id);
|
|
52
|
+
return source[id].componentId === 'layoutSlot' || childrenOf(source[id]).some((child)=>holdsSlot(child, seen));
|
|
53
|
+
};
|
|
54
|
+
const grower = top.find((id)=>holdsSlot(id));
|
|
55
|
+
if (!grower) return tree;
|
|
56
|
+
let frameId = AI_LAYOUT_FRAME_ID;
|
|
57
|
+
for(let n = 2; frameId in source; n += 1)frameId = `${AI_LAYOUT_FRAME_ID}${n}`;
|
|
58
|
+
const nodes = _extends({}, source, {
|
|
59
|
+
[rootId]: _extends({}, source[rootId], {
|
|
60
|
+
nodes: [
|
|
61
|
+
frameId
|
|
62
|
+
]
|
|
63
|
+
}),
|
|
64
|
+
[frameId]: {
|
|
65
|
+
componentId: 'muiStack',
|
|
66
|
+
sx: {
|
|
67
|
+
minHeight: '100vh'
|
|
68
|
+
},
|
|
69
|
+
nodes: top
|
|
70
|
+
},
|
|
71
|
+
[grower]: _extends({}, source[grower], {
|
|
72
|
+
sx: _extends({}, (_source_grower_sx = source[grower].sx) != null ? _source_grower_sx : {}, {
|
|
73
|
+
flexGrow: 1
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
return _extends({}, tree, {
|
|
78
|
+
nodes
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=ai-layout-full-height.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-layout-full-height.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A layout at least as tall as the window (AGL-3596).\n *\n * A layout is header, page, footer, top to bottom, and on a page shorter than\n * the window the footer used to end wherever the page did — on beta.230 a\n * contact page left its footer floating halfway up the screen with blank page\n * under it. So every layout the AI builds is written as one column at least\n * the window's height, and the part of it that holds the Layout Slot takes\n * the room left over: the footer sits at the bottom of a short page and\n * follows the content of a long one.\n *\n * Written by the platform after the model answers and before the doctrine\n * checks the tree, so the doctrine holds the column like anything else: it is\n * a Stack with a style of its own, which no rule calls an empty wrapper.\n */\n\n/** The id of the column the platform adds, when no node of the tree has it. */\nexport const AI_LAYOUT_FRAME_ID = 'aiLayoutFrame'\n\ntype RawNode = { componentId?: unknown; props?: Record<string, unknown>; sx?: Record<string, unknown>; nodes?: unknown }\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n\nconst childrenOf = (node: RawNode | undefined): string[] =>\n Array.isArray(node?.nodes) ? (node.nodes as unknown[]).filter((id): id is string => typeof id === 'string') : []\n\n/**\n * The layout with its root's children in a column at least the window's\n * height, and the child that holds the Layout Slot growing into the room\n * left. A tree whose top already sets a minimum height is left as it came,\n * and so is anything that is not a node map or holds no Layout Slot.\n */\nexport function aiLayoutWithFullHeight(tree: unknown): unknown {\n if (!isRecord(tree) || !isRecord(tree['nodes'])) return tree\n const source = tree['nodes'] as Record<string, RawNode>\n const rootId = typeof tree['rootId'] === 'string' ? tree['rootId'] : null\n if (!rootId || !isRecord(source[rootId])) return tree\n const top = childrenOf(source[rootId]).filter((id) => isRecord(source[id]))\n if (!top.length) return tree\n if (top.length === 1 && source[top[0]].sx?.['minHeight'] !== undefined) return tree\n\n const holdsSlot = (id: string, seen = new Set<string>()): boolean => {\n if (seen.has(id) || !isRecord(source[id])) return false\n seen.add(id)\n return source[id].componentId === 'layoutSlot' || childrenOf(source[id]).some((child) => holdsSlot(child, seen))\n }\n const grower = top.find((id) => holdsSlot(id))\n if (!grower) return tree\n\n let frameId = AI_LAYOUT_FRAME_ID\n for (let n = 2; frameId in source; n += 1) frameId = `${AI_LAYOUT_FRAME_ID}${n}`\n const nodes: Record<string, RawNode> = {\n ...source,\n [rootId]: { ...source[rootId], nodes: [frameId] },\n [frameId]: { componentId: 'muiStack', sx: { minHeight: '100vh' }, nodes: top },\n [grower]: { ...source[grower], sx: { ...(source[grower].sx ?? {}), flexGrow: 1 } },\n }\n return { ...tree, nodes }\n}\n"],"names":["AI_LAYOUT_FRAME_ID","isRecord","value","Boolean","Array","isArray","childrenOf","node","nodes","filter","id","aiLayoutWithFullHeight","tree","source","rootId","top","length","sx","undefined","holdsSlot","seen","Set","has","add","componentId","some","child","grower","find","frameId","n","minHeight","flexGrow"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;CAcC,GAED,6EAA6E,GAC7E,OAAO,MAAMA,qBAAqB,gBAAe;AAIjD,MAAMC,WAAW,CAACC,QAChBC,QAAQD,UAAU,OAAOA,UAAU,YAAY,CAACE,MAAMC,OAAO,CAACH;AAEhE,MAAMI,aAAa,CAACC,OAClBH,MAAMC,OAAO,CAACE,wBAAAA,KAAMC,KAAK,IAAI,AAACD,KAAKC,KAAK,CAAeC,MAAM,CAAC,CAACC,KAAqB,OAAOA,OAAO,YAAY,EAAE;AAElH;;;;;CAKC,GACD,OAAO,SAASC,uBAAuBC,IAAa;QAuBPC;QAhBnBA;IANxB,IAAI,CAACZ,SAASW,SAAS,CAACX,SAASW,IAAI,CAAC,QAAQ,GAAG,OAAOA;IACxD,MAAMC,SAASD,IAAI,CAAC,QAAQ;IAC5B,MAAME,SAAS,OAAOF,IAAI,CAAC,SAAS,KAAK,WAAWA,IAAI,CAAC,SAAS,GAAG;IACrE,IAAI,CAACE,UAAU,CAACb,SAASY,MAAM,CAACC,OAAO,GAAG,OAAOF;IACjD,MAAMG,MAAMT,WAAWO,MAAM,CAACC,OAAO,EAAEL,MAAM,CAAC,CAACC,KAAOT,SAASY,MAAM,CAACH,GAAG;IACzE,IAAI,CAACK,IAAIC,MAAM,EAAE,OAAOJ;IACxB,IAAIG,IAAIC,MAAM,KAAK,KAAKH,EAAAA,kBAAAA,MAAM,CAACE,GAAG,CAAC,EAAE,CAAC,CAACE,EAAE,qBAAjBJ,eAAmB,CAAC,YAAY,MAAKK,WAAW,OAAON;IAE/E,MAAMO,YAAY,CAACT,IAAYU,OAAO,IAAIC,KAAa;QACrD,IAAID,KAAKE,GAAG,CAACZ,OAAO,CAACT,SAASY,MAAM,CAACH,GAAG,GAAG,OAAO;QAClDU,KAAKG,GAAG,CAACb;QACT,OAAOG,MAAM,CAACH,GAAG,CAACc,WAAW,KAAK,gBAAgBlB,WAAWO,MAAM,CAACH,GAAG,EAAEe,IAAI,CAAC,CAACC,QAAUP,UAAUO,OAAON;IAC5G;IACA,MAAMO,SAASZ,IAAIa,IAAI,CAAC,CAAClB,KAAOS,UAAUT;IAC1C,IAAI,CAACiB,QAAQ,OAAOf;IAEpB,IAAIiB,UAAU7B;IACd,IAAK,IAAI8B,IAAI,GAAGD,WAAWhB,QAAQiB,KAAK,EAAGD,UAAU,GAAG7B,qBAAqB8B,GAAG;IAChF,MAAMtB,QAAiC,aAClCK;QACH,CAACC,OAAO,EAAE,aAAKD,MAAM,CAACC,OAAO;YAAEN,OAAO;gBAACqB;aAAQ;;QAC/C,CAACA,QAAQ,EAAE;YAAEL,aAAa;YAAYP,IAAI;gBAAEc,WAAW;YAAQ;YAAGvB,OAAOO;QAAI;QAC7E,CAACY,OAAO,EAAE,aAAKd,MAAM,CAACc,OAAO;YAAEV,IAAI,cAAMJ,oBAAAA,MAAM,CAACc,OAAO,CAACV,EAAE,YAAjBJ,oBAAqB,CAAC;gBAAImB,UAAU;;;;IAE/E,OAAO,aAAKpB;QAAMJ;;AACpB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
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 { AiDoctrineTree, AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
|
|
18
|
+
/**
|
|
19
|
+
* What a layout may say about the business it frames (AGL-3596).
|
|
20
|
+
*
|
|
21
|
+
* A layout's header and footer are on every page, so a fact in them is
|
|
22
|
+
* published everywhere at once. Two went wrong on beta.230: the model coined
|
|
23
|
+
* a name for the business ("Austin Paws Grooming" for a site called Hillside
|
|
24
|
+
* Dog Grooming), and a footer is exactly where a model reaches for a phone
|
|
25
|
+
* number, an email or opening hours the brief never gave.
|
|
26
|
+
*
|
|
27
|
+
* - The site's name is the brand, and it is written as the host token that
|
|
28
|
+
* reads it (`{{host.businessName}}`), so a rename in Setup follows on every
|
|
29
|
+
* page rather than leaving the name it was built under
|
|
30
|
+
* (`aiLayoutWithSiteName`).
|
|
31
|
+
* - A contact detail the brief does not give is refused by the layout's own
|
|
32
|
+
* check (`aiLayoutInventedContactViolations`), so the model is asked again
|
|
33
|
+
* to leave it out: an email, a phone number, a street address or opening
|
|
34
|
+
* hours appear in the layout only when the brief has them.
|
|
35
|
+
*/
|
|
36
|
+
/** The host token that reads a site's own name at render. */
|
|
37
|
+
export declare const AI_SITE_NAME_TOKEN = "{{host.businessName}}";
|
|
38
|
+
/**
|
|
39
|
+
* The tree with every written occurrence of the site's name — the brand, the
|
|
40
|
+
* copyright line — replaced by the host token that reads it. Matched whole,
|
|
41
|
+
* ignoring case and runs of spaces. Anything that is not a node map, and a
|
|
42
|
+
* name too short to match safely, leaves the tree as it came.
|
|
43
|
+
*/
|
|
44
|
+
export declare function aiLayoutWithSiteName(tree: unknown, siteName: string | null | undefined): unknown;
|
|
45
|
+
/**
|
|
46
|
+
* Rule 14 for a layout: every email address, phone number, street address
|
|
47
|
+
* and opening time its words carry is one the brief gives. A number is read
|
|
48
|
+
* by its digits, so a brief's "512.555.0142" admits "(512) 555-0142"; a year
|
|
49
|
+
* or a price is not a phone number, so only a run of seven digits or more is
|
|
50
|
+
* read as one. An address is matched by its house number and first street
|
|
51
|
+
* word; hours are admitted when the brief gives any, since a brief that names
|
|
52
|
+
* its hours is spelled a dozen ways a footer may fairly reword.
|
|
53
|
+
*/
|
|
54
|
+
export declare function aiLayoutInventedContactViolations(tree: AiDoctrineTree, brief: string): AiDoctrineViolation[];
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
import { walkTree } from "../runtime/ai-doctrine-validators.js";
|
|
18
|
+
/**
|
|
19
|
+
* What a layout may say about the business it frames (AGL-3596).
|
|
20
|
+
*
|
|
21
|
+
* A layout's header and footer are on every page, so a fact in them is
|
|
22
|
+
* published everywhere at once. Two went wrong on beta.230: the model coined
|
|
23
|
+
* a name for the business ("Austin Paws Grooming" for a site called Hillside
|
|
24
|
+
* Dog Grooming), and a footer is exactly where a model reaches for a phone
|
|
25
|
+
* number, an email or opening hours the brief never gave.
|
|
26
|
+
*
|
|
27
|
+
* - The site's name is the brand, and it is written as the host token that
|
|
28
|
+
* reads it (`{{host.businessName}}`), so a rename in Setup follows on every
|
|
29
|
+
* page rather than leaving the name it was built under
|
|
30
|
+
* (`aiLayoutWithSiteName`).
|
|
31
|
+
* - A contact detail the brief does not give is refused by the layout's own
|
|
32
|
+
* check (`aiLayoutInventedContactViolations`), so the model is asked again
|
|
33
|
+
* to leave it out: an email, a phone number, a street address or opening
|
|
34
|
+
* hours appear in the layout only when the brief has them.
|
|
35
|
+
*/ /** The host token that reads a site's own name at render. */ export const AI_SITE_NAME_TOKEN = '{{host.businessName}}';
|
|
36
|
+
const isRecord = (value)=>Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
37
|
+
const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
38
|
+
/**
|
|
39
|
+
* The tree with every written occurrence of the site's name — the brand, the
|
|
40
|
+
* copyright line — replaced by the host token that reads it. Matched whole,
|
|
41
|
+
* ignoring case and runs of spaces. Anything that is not a node map, and a
|
|
42
|
+
* name too short to match safely, leaves the tree as it came.
|
|
43
|
+
*/ export function aiLayoutWithSiteName(tree, siteName) {
|
|
44
|
+
const name = (siteName != null ? siteName : '').replace(/\s+/g, ' ').trim();
|
|
45
|
+
if (name.length < 3 || !isRecord(tree) || !isRecord(tree['nodes'])) return tree;
|
|
46
|
+
const pattern = new RegExp(`(?<![\\p{L}\\p{N}])${escapeRegExp(name).replace(/ /g, '\\s+')}(?![\\p{L}\\p{N}])`, 'giu');
|
|
47
|
+
let changed = false;
|
|
48
|
+
const nodes = {};
|
|
49
|
+
for (const [id, node] of Object.entries(tree['nodes'])){
|
|
50
|
+
if (!isRecord(node) || !isRecord(node['props'])) {
|
|
51
|
+
nodes[id] = node;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const props = {};
|
|
55
|
+
let touched = false;
|
|
56
|
+
for (const [key, value] of Object.entries(node['props'])){
|
|
57
|
+
const next = typeof value === 'string' ? value.replace(pattern, AI_SITE_NAME_TOKEN) : value;
|
|
58
|
+
if (next !== value) touched = true;
|
|
59
|
+
props[key] = next;
|
|
60
|
+
}
|
|
61
|
+
nodes[id] = touched ? _extends({}, node, {
|
|
62
|
+
props
|
|
63
|
+
}) : node;
|
|
64
|
+
changed || (changed = touched);
|
|
65
|
+
}
|
|
66
|
+
return changed ? _extends({}, tree, {
|
|
67
|
+
nodes
|
|
68
|
+
}) : tree;
|
|
69
|
+
}
|
|
70
|
+
const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
71
|
+
/** A phone number as people write one: seven or more digits, with the usual separators between them. */ const PHONE = /(?:\+?\(?\d[\d\s().-]{5,}\d)/g;
|
|
72
|
+
const digitsOf = (value)=>value.replace(/\D/g, '');
|
|
73
|
+
const STREET_SUFFIX = 'Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd|Lane|Ln|Drive|Dr|Way|Court|Ct|Place|Pl|Parkway|Pkwy|Highway|Hwy|Trail|Trl|Circle|Cir|Square|Sq';
|
|
74
|
+
/** A street address as a footer writes one: a house number, one to four words, and a street suffix. */ const ADDRESS = new RegExp(`\\b\\d{1,6}\\s+(?:[A-Z0-9][\\w'.-]*\\s+){1,4}(?:${STREET_SUFFIX})\\b\\.?`, 'g');
|
|
75
|
+
const DAY = '(?:Mon(?:day)?|Tue(?:s|sday)?|Wed(?:nesday)?|Thu(?:r|rs|rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)';
|
|
76
|
+
/**
|
|
77
|
+
* Opening hours: a time of day ("9am", "8:30 PM") or a range of days
|
|
78
|
+
* ("Mon–Fri", "Monday through Saturday"). A bare "9–5" is left alone, since
|
|
79
|
+
* "2–3 weeks" is written the same way.
|
|
80
|
+
*/ const HOURS = new RegExp([
|
|
81
|
+
'\\b\\d{1,2}(?::\\d{2})?\\s?(?:[ap]\\.?m\\.?)(?![a-z])',
|
|
82
|
+
`\\b${DAY}\\s?(?:[–—-]|to|through|thru)\\s?${DAY}\\b`
|
|
83
|
+
].join('|'), 'gi');
|
|
84
|
+
/** SVG path data: a move command, then only commands and numbers. */ const SVG_PATH_DATA = /^\s*[Mm][\s\d.,eE+-]*[\d.][\s\d.,eE+\-MmLlHhVvCcSsQqTtAaZz]*$/;
|
|
85
|
+
/** The words of an address, as a brief and a layout can both spell them. */ const addressKey = (value)=>{
|
|
86
|
+
var _value_toLowerCase_match;
|
|
87
|
+
const [number, word] = (_value_toLowerCase_match = value.toLowerCase().match(/[a-z0-9]+/g)) != null ? _value_toLowerCase_match : [];
|
|
88
|
+
return `${number != null ? number : ''} ${word != null ? word : ''}`;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The contact details a brief gives: its email addresses, the digits of its
|
|
92
|
+
* phone numbers, the house number and first word of its street addresses,
|
|
93
|
+
* and whether it gives opening hours at all.
|
|
94
|
+
*/ function briefFacts(brief) {
|
|
95
|
+
var _brief_match, _brief_match1;
|
|
96
|
+
return {
|
|
97
|
+
emails: new Set(((_brief_match = brief.match(EMAIL)) != null ? _brief_match : []).map((email)=>email.toLowerCase())),
|
|
98
|
+
digits: digitsOf(brief),
|
|
99
|
+
addresses: new Set(((_brief_match1 = brief.match(ADDRESS)) != null ? _brief_match1 : []).map(addressKey)),
|
|
100
|
+
hours: new RegExp(HOURS.source, 'i').test(brief)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Rule 14 for a layout: every email address, phone number, street address
|
|
105
|
+
* and opening time its words carry is one the brief gives. A number is read
|
|
106
|
+
* by its digits, so a brief's "512.555.0142" admits "(512) 555-0142"; a year
|
|
107
|
+
* or a price is not a phone number, so only a run of seven digits or more is
|
|
108
|
+
* read as one. An address is matched by its house number and first street
|
|
109
|
+
* word; hours are admitted when the brief gives any, since a brief that names
|
|
110
|
+
* its hours is spelled a dozen ways a footer may fairly reword.
|
|
111
|
+
*/ export function aiLayoutInventedContactViolations(tree, brief) {
|
|
112
|
+
const given = briefFacts(brief);
|
|
113
|
+
const invented = [];
|
|
114
|
+
const nodeIds = [];
|
|
115
|
+
for (const { id, node } of walkTree(tree)){
|
|
116
|
+
var _node_props;
|
|
117
|
+
for (const value of Object.values((_node_props = node.props) != null ? _node_props : {})){
|
|
118
|
+
// An icon's drawing (`iconPath`) is SVG path data, whose coordinates read as phone numbers.
|
|
119
|
+
if (typeof value !== 'string' || SVG_PATH_DATA.test(value)) continue;
|
|
120
|
+
const matches = (pattern)=>{
|
|
121
|
+
var _value_match;
|
|
122
|
+
return [
|
|
123
|
+
...(_value_match = value.match(pattern)) != null ? _value_match : []
|
|
124
|
+
];
|
|
125
|
+
};
|
|
126
|
+
const found = [
|
|
127
|
+
...matches(EMAIL).filter((email)=>!given.emails.has(email.toLowerCase())),
|
|
128
|
+
...matches(PHONE).filter((phone)=>{
|
|
129
|
+
const digits = digitsOf(phone);
|
|
130
|
+
return digits.length >= 7 && !given.digits.includes(digits);
|
|
131
|
+
}),
|
|
132
|
+
...matches(ADDRESS).filter((address)=>!given.addresses.has(addressKey(address))),
|
|
133
|
+
...given.hours ? [] : matches(HOURS)
|
|
134
|
+
];
|
|
135
|
+
if (!found.length) continue;
|
|
136
|
+
invented.push(...found.map((fact)=>fact.trim()));
|
|
137
|
+
if (!nodeIds.includes(id)) nodeIds.push(id);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (!invented.length) return [];
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
rule: 14,
|
|
144
|
+
code: 'contact-not-in-brief',
|
|
145
|
+
message: `${[
|
|
146
|
+
...new Set(invented)
|
|
147
|
+
].map((fact)=>`"${fact}"`).join(', ')} is not in the brief. A layout shows only the contact details the brief gives: leave this out, and never invent one.`,
|
|
148
|
+
nodeIds
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=ai-layout-site-facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-layout-site-facts.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 { AiDoctrineNode, AiDoctrineTree, AiDoctrineViolation } from '../runtime/ai-doctrine-validators'\nimport { walkTree } from '../runtime/ai-doctrine-validators'\n\n/**\n * What a layout may say about the business it frames (AGL-3596).\n *\n * A layout's header and footer are on every page, so a fact in them is\n * published everywhere at once. Two went wrong on beta.230: the model coined\n * a name for the business (\"Austin Paws Grooming\" for a site called Hillside\n * Dog Grooming), and a footer is exactly where a model reaches for a phone\n * number, an email or opening hours the brief never gave.\n *\n * - The site's name is the brand, and it is written as the host token that\n * reads it (`{{host.businessName}}`), so a rename in Setup follows on every\n * page rather than leaving the name it was built under\n * (`aiLayoutWithSiteName`).\n * - A contact detail the brief does not give is refused by the layout's own\n * check (`aiLayoutInventedContactViolations`), so the model is asked again\n * to leave it out: an email, a phone number, a street address or opening\n * hours appear in the layout only when the brief has them.\n */\n\n/** The host token that reads a site's own name at render. */\nexport const AI_SITE_NAME_TOKEN = '{{host.businessName}}'\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n\nconst escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n/**\n * The tree with every written occurrence of the site's name — the brand, the\n * copyright line — replaced by the host token that reads it. Matched whole,\n * ignoring case and runs of spaces. Anything that is not a node map, and a\n * name too short to match safely, leaves the tree as it came.\n */\nexport function aiLayoutWithSiteName(tree: unknown, siteName: string | null | undefined): unknown {\n const name = (siteName ?? '').replace(/\\s+/g, ' ').trim()\n if (name.length < 3 || !isRecord(tree) || !isRecord(tree['nodes'])) return tree\n const pattern = new RegExp(`(?<![\\\\p{L}\\\\p{N}])${escapeRegExp(name).replace(/ /g, '\\\\s+')}(?![\\\\p{L}\\\\p{N}])`, 'giu')\n let changed = false\n const nodes: Record<string, unknown> = {}\n for (const [id, node] of Object.entries(tree['nodes'])) {\n if (!isRecord(node) || !isRecord(node['props'])) {\n nodes[id] = node\n continue\n }\n const props: Record<string, unknown> = {}\n let touched = false\n for (const [key, value] of Object.entries(node['props'])) {\n const next = typeof value === 'string' ? value.replace(pattern, AI_SITE_NAME_TOKEN) : value\n if (next !== value) touched = true\n props[key] = next\n }\n nodes[id] = touched ? { ...node, props } : node\n changed ||= touched\n }\n return changed ? { ...tree, nodes } : tree\n}\n\nconst EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g\n/** A phone number as people write one: seven or more digits, with the usual separators between them. */\nconst PHONE = /(?:\\+?\\(?\\d[\\d\\s().-]{5,}\\d)/g\n\nconst digitsOf = (value: string) => value.replace(/\\D/g, '')\n\nconst STREET_SUFFIX =\n 'Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd|Lane|Ln|Drive|Dr|Way|Court|Ct|Place|Pl|Parkway|Pkwy|Highway|Hwy|Trail|Trl|Circle|Cir|Square|Sq'\n/** A street address as a footer writes one: a house number, one to four words, and a street suffix. */\nconst ADDRESS = new RegExp(`\\\\b\\\\d{1,6}\\\\s+(?:[A-Z0-9][\\\\w'.-]*\\\\s+){1,4}(?:${STREET_SUFFIX})\\\\b\\\\.?`, 'g')\nconst DAY = '(?:Mon(?:day)?|Tue(?:s|sday)?|Wed(?:nesday)?|Thu(?:r|rs|rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)'\n/**\n * Opening hours: a time of day (\"9am\", \"8:30 PM\") or a range of days\n * (\"Mon–Fri\", \"Monday through Saturday\"). A bare \"9–5\" is left alone, since\n * \"2–3 weeks\" is written the same way.\n */\nconst HOURS = new RegExp(\n [\n '\\\\b\\\\d{1,2}(?::\\\\d{2})?\\\\s?(?:[ap]\\\\.?m\\\\.?)(?![a-z])',\n `\\\\b${DAY}\\\\s?(?:[–—-]|to|through|thru)\\\\s?${DAY}\\\\b`,\n ].join('|'),\n 'gi',\n)\n\n/** SVG path data: a move command, then only commands and numbers. */\nconst SVG_PATH_DATA = /^\\s*[Mm][\\s\\d.,eE+-]*[\\d.][\\s\\d.,eE+\\-MmLlHhVvCcSsQqTtAaZz]*$/\n\n/** The words of an address, as a brief and a layout can both spell them. */\nconst addressKey = (value: string) => {\n const [number, word] = value.toLowerCase().match(/[a-z0-9]+/g) ?? []\n return `${number ?? ''} ${word ?? ''}`\n}\n\n/**\n * The contact details a brief gives: its email addresses, the digits of its\n * phone numbers, the house number and first word of its street addresses,\n * and whether it gives opening hours at all.\n */\nfunction briefFacts(brief: string): { emails: Set<string>; digits: string; addresses: Set<string>; hours: boolean } {\n return {\n emails: new Set((brief.match(EMAIL) ?? []).map((email) => email.toLowerCase())),\n digits: digitsOf(brief),\n addresses: new Set((brief.match(ADDRESS) ?? []).map(addressKey)),\n hours: new RegExp(HOURS.source, 'i').test(brief),\n }\n}\n\n/**\n * Rule 14 for a layout: every email address, phone number, street address\n * and opening time its words carry is one the brief gives. A number is read\n * by its digits, so a brief's \"512.555.0142\" admits \"(512) 555-0142\"; a year\n * or a price is not a phone number, so only a run of seven digits or more is\n * read as one. An address is matched by its house number and first street\n * word; hours are admitted when the brief gives any, since a brief that names\n * its hours is spelled a dozen ways a footer may fairly reword.\n */\nexport function aiLayoutInventedContactViolations(tree: AiDoctrineTree, brief: string): AiDoctrineViolation[] {\n const given = briefFacts(brief)\n const invented: string[] = []\n const nodeIds: string[] = []\n for (const { id, node } of walkTree(tree)) {\n for (const value of Object.values((node as AiDoctrineNode).props ?? {})) {\n // An icon's drawing (`iconPath`) is SVG path data, whose coordinates read as phone numbers.\n if (typeof value !== 'string' || SVG_PATH_DATA.test(value)) continue\n const matches = (pattern: RegExp): string[] => [...(value.match(pattern) ?? [])]\n const found: string[] = [\n ...matches(EMAIL).filter((email) => !given.emails.has(email.toLowerCase())),\n ...matches(PHONE).filter((phone) => {\n const digits = digitsOf(phone)\n return digits.length >= 7 && !given.digits.includes(digits)\n }),\n ...matches(ADDRESS).filter((address) => !given.addresses.has(addressKey(address))),\n ...(given.hours ? [] : matches(HOURS)),\n ]\n if (!found.length) continue\n invented.push(...found.map((fact) => fact.trim()))\n if (!nodeIds.includes(id)) nodeIds.push(id)\n }\n }\n if (!invented.length) return []\n return [\n {\n rule: 14,\n code: 'contact-not-in-brief',\n message: `${[...new Set(invented)].map((fact) => `\"${fact}\"`).join(', ')} is not in the brief. A layout shows only the contact details the brief gives: leave this out, and never invent one.`,\n nodeIds,\n },\n ]\n}\n"],"names":["walkTree","AI_SITE_NAME_TOKEN","isRecord","value","Boolean","Array","isArray","escapeRegExp","replace","aiLayoutWithSiteName","tree","siteName","name","trim","length","pattern","RegExp","changed","nodes","id","node","Object","entries","props","touched","key","next","EMAIL","PHONE","digitsOf","STREET_SUFFIX","ADDRESS","DAY","HOURS","join","SVG_PATH_DATA","addressKey","number","word","toLowerCase","match","briefFacts","brief","emails","Set","map","email","digits","addresses","hours","source","test","aiLayoutInventedContactViolations","given","invented","nodeIds","values","matches","found","filter","has","phone","includes","address","push","fact","rule","code","message"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,QAAQ,QAAQ,uCAAmC;AAE5D;;;;;;;;;;;;;;;;;CAiBC,GAED,2DAA2D,GAC3D,OAAO,MAAMC,qBAAqB,wBAAuB;AAEzD,MAAMC,WAAW,CAACC,QAChBC,QAAQD,UAAU,OAAOA,UAAU,YAAY,CAACE,MAAMC,OAAO,CAACH;AAEhE,MAAMI,eAAe,CAACJ,QAAkBA,MAAMK,OAAO,CAAC,uBAAuB;AAE7E;;;;;CAKC,GACD,OAAO,SAASC,qBAAqBC,IAAa,EAAEC,QAAmC;IACrF,MAAMC,OAAO,CAACD,mBAAAA,WAAY,IAAIH,OAAO,CAAC,QAAQ,KAAKK,IAAI;IACvD,IAAID,KAAKE,MAAM,GAAG,KAAK,CAACZ,SAASQ,SAAS,CAACR,SAASQ,IAAI,CAAC,QAAQ,GAAG,OAAOA;IAC3E,MAAMK,UAAU,IAAIC,OAAO,CAAC,mBAAmB,EAAET,aAAaK,MAAMJ,OAAO,CAAC,MAAM,QAAQ,kBAAkB,CAAC,EAAE;IAC/G,IAAIS,UAAU;IACd,MAAMC,QAAiC,CAAC;IACxC,KAAK,MAAM,CAACC,IAAIC,KAAK,IAAIC,OAAOC,OAAO,CAACZ,IAAI,CAAC,QAAQ,EAAG;QACtD,IAAI,CAACR,SAASkB,SAAS,CAAClB,SAASkB,IAAI,CAAC,QAAQ,GAAG;YAC/CF,KAAK,CAACC,GAAG,GAAGC;YACZ;QACF;QACA,MAAMG,QAAiC,CAAC;QACxC,IAAIC,UAAU;QACd,KAAK,MAAM,CAACC,KAAKtB,MAAM,IAAIkB,OAAOC,OAAO,CAACF,IAAI,CAAC,QAAQ,EAAG;YACxD,MAAMM,OAAO,OAAOvB,UAAU,WAAWA,MAAMK,OAAO,CAACO,SAASd,sBAAsBE;YACtF,IAAIuB,SAASvB,OAAOqB,UAAU;YAC9BD,KAAK,CAACE,IAAI,GAAGC;QACf;QACAR,KAAK,CAACC,GAAG,GAAGK,UAAU,aAAKJ;YAAMG;aAAUH;QAC3CH,YAAAA,UAAYO;IACd;IACA,OAAOP,UAAU,aAAKP;QAAMQ;SAAUR;AACxC;AAEA,MAAMiB,QAAQ;AACd,sGAAsG,GACtG,MAAMC,QAAQ;AAEd,MAAMC,WAAW,CAAC1B,QAAkBA,MAAMK,OAAO,CAAC,OAAO;AAEzD,MAAMsB,gBACJ;AACF,qGAAqG,GACrG,MAAMC,UAAU,IAAIf,OAAO,CAAC,gDAAgD,EAAEc,cAAc,QAAQ,CAAC,EAAE;AACvG,MAAME,MAAM;AACZ;;;;CAIC,GACD,MAAMC,QAAQ,IAAIjB,OAChB;IACE;IACA,CAAC,GAAG,EAAEgB,IAAI,iCAAiC,EAAEA,IAAI,GAAG,CAAC;CACtD,CAACE,IAAI,CAAC,MACP;AAGF,mEAAmE,GACnE,MAAMC,gBAAgB;AAEtB,0EAA0E,GAC1E,MAAMC,aAAa,CAACjC;QACKA;IAAvB,MAAM,CAACkC,QAAQC,KAAK,IAAGnC,2BAAAA,MAAMoC,WAAW,GAAGC,KAAK,CAAC,yBAA1BrC,2BAA2C,EAAE;IACpE,OAAO,GAAGkC,iBAAAA,SAAU,GAAG,CAAC,EAAEC,eAAAA,OAAQ,IAAI;AACxC;AAEA;;;;CAIC,GACD,SAASG,WAAWC,KAAa;QAEZA,cAEGA;IAHtB,OAAO;QACLC,QAAQ,IAAIC,IAAI,EAACF,eAAAA,MAAMF,KAAK,CAACb,kBAAZe,eAAsB,EAAE,EAAEG,GAAG,CAAC,CAACC,QAAUA,MAAMP,WAAW;QAC3EQ,QAAQlB,SAASa;QACjBM,WAAW,IAAIJ,IAAI,EAACF,gBAAAA,MAAMF,KAAK,CAACT,oBAAZW,gBAAwB,EAAE,EAAEG,GAAG,CAACT;QACpDa,OAAO,IAAIjC,OAAOiB,MAAMiB,MAAM,EAAE,KAAKC,IAAI,CAACT;IAC5C;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASU,kCAAkC1C,IAAoB,EAAEgC,KAAa;IACnF,MAAMW,QAAQZ,WAAWC;IACzB,MAAMY,WAAqB,EAAE;IAC7B,MAAMC,UAAoB,EAAE;IAC5B,KAAK,MAAM,EAAEpC,EAAE,EAAEC,IAAI,EAAE,IAAIpB,SAASU,MAAO;YACP;QAAlC,KAAK,MAAMP,SAASkB,OAAOmC,MAAM,EAAC,cAAA,AAACpC,KAAwBG,KAAK,YAA9B,cAAkC,CAAC,GAAI;YACvE,4FAA4F;YAC5F,IAAI,OAAOpB,UAAU,YAAYgC,cAAcgB,IAAI,CAAChD,QAAQ;YAC5D,MAAMsD,UAAU,CAAC1C;oBAAmCZ;uBAAL;wBAAKA,eAAAA,MAAMqC,KAAK,CAACzB,oBAAZZ,eAAwB,EAAE;iBAAE;;YAChF,MAAMuD,QAAkB;mBACnBD,QAAQ9B,OAAOgC,MAAM,CAAC,CAACb,QAAU,CAACO,MAAMV,MAAM,CAACiB,GAAG,CAACd,MAAMP,WAAW;mBACpEkB,QAAQ7B,OAAO+B,MAAM,CAAC,CAACE;oBACxB,MAAMd,SAASlB,SAASgC;oBACxB,OAAOd,OAAOjC,MAAM,IAAI,KAAK,CAACuC,MAAMN,MAAM,CAACe,QAAQ,CAACf;gBACtD;mBACGU,QAAQ1B,SAAS4B,MAAM,CAAC,CAACI,UAAY,CAACV,MAAML,SAAS,CAACY,GAAG,CAACxB,WAAW2B;mBACpEV,MAAMJ,KAAK,GAAG,EAAE,GAAGQ,QAAQxB;aAChC;YACD,IAAI,CAACyB,MAAM5C,MAAM,EAAE;YACnBwC,SAASU,IAAI,IAAIN,MAAMb,GAAG,CAAC,CAACoB,OAASA,KAAKpD,IAAI;YAC9C,IAAI,CAAC0C,QAAQO,QAAQ,CAAC3C,KAAKoC,QAAQS,IAAI,CAAC7C;QAC1C;IACF;IACA,IAAI,CAACmC,SAASxC,MAAM,EAAE,OAAO,EAAE;IAC/B,OAAO;QACL;YACEoD,MAAM;YACNC,MAAM;YACNC,SAAS,GAAG;mBAAI,IAAIxB,IAAIU;aAAU,CAACT,GAAG,CAAC,CAACoB,OAAS,CAAC,CAAC,EAAEA,KAAK,CAAC,CAAC,EAAE/B,IAAI,CAAC,MAAM,oHAAoH,CAAC;YAC9LqB;QACF;KACD;AACH"}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 { HostFunction } from '@aglyn/aglyn/app-utils/functions';
|
|
18
|
+
import type { AiLogicSiteVariable } from '../model/ai-logic-job';
|
|
19
|
+
/**
|
|
20
|
+
* What a `logic` job reads (AGL-3603): the site's variables and function
|
|
21
|
+
* names, in the window the Functions & Variables page itself reads, and one
|
|
22
|
+
* saved function by id. Functions and variables are core documents
|
|
23
|
+
* (`hosts/{hostId}/functions`, `hosts/{hostId}/variables`), read through the
|
|
24
|
+
* Admin SDK as projections of the fields named here.
|
|
25
|
+
*/
|
|
26
|
+
type Firestore = FirebaseFirestore.Firestore;
|
|
27
|
+
/** Records of one kind a job reads: the logic page's own ceiling. */
|
|
28
|
+
export declare const AI_LOGIC_RECORDS_WINDOW = 100;
|
|
29
|
+
export interface AiLogicRecords {
|
|
30
|
+
variables: AiLogicSiteVariable[];
|
|
31
|
+
functions: string[];
|
|
32
|
+
}
|
|
33
|
+
/** The site's live variables (name, type, value) and function names. */
|
|
34
|
+
export declare function readAiLogicRecords(firestore: Firestore, hostId: string): Promise<AiLogicRecords>;
|
|
35
|
+
/** One saved function of the site, or `null` for one that does not exist or was deleted. */
|
|
36
|
+
export declare function readAiLogicFunction(firestore: Firestore, input: {
|
|
37
|
+
hostId: string;
|
|
38
|
+
id: string;
|
|
39
|
+
}): Promise<HostFunction | null>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
/** Records of one kind a job reads: the logic page's own ceiling. */ export const AI_LOGIC_RECORDS_WINDOW = 100;
|
|
18
|
+
const live = (data)=>data['deletedAt'] == null;
|
|
19
|
+
/** The site's live variables (name, type, value) and function names. */ export async function readAiLogicRecords(firestore, hostId) {
|
|
20
|
+
const host = firestore.collection('hosts').doc(hostId);
|
|
21
|
+
const [variables, functions] = await Promise.all([
|
|
22
|
+
host.collection('variables').select('name', 'type', 'value', 'deletedAt').limit(AI_LOGIC_RECORDS_WINDOW).get(),
|
|
23
|
+
host.collection('functions').select('name', 'deletedAt').limit(AI_LOGIC_RECORDS_WINDOW).get()
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
variables: variables.docs.map((doc)=>{
|
|
27
|
+
var _doc_data;
|
|
28
|
+
return (_doc_data = doc.data()) != null ? _doc_data : {};
|
|
29
|
+
}).filter(live).filter((data)=>typeof data['name'] === 'string' && data['name']).map((data)=>({
|
|
30
|
+
name: String(data['name']),
|
|
31
|
+
type: typeof data['type'] === 'string' ? data['type'] : 'text',
|
|
32
|
+
value: typeof data['value'] === 'string' ? data['value'] : ''
|
|
33
|
+
})),
|
|
34
|
+
functions: functions.docs.map((doc)=>{
|
|
35
|
+
var _doc_data;
|
|
36
|
+
return (_doc_data = doc.data()) != null ? _doc_data : {};
|
|
37
|
+
}).filter(live).map((data)=>typeof data['name'] === 'string' ? data['name'].trim() : '').filter(Boolean)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** One saved function of the site, or `null` for one that does not exist or was deleted. */ export async function readAiLogicFunction(firestore, input) {
|
|
41
|
+
var _snapshot_data;
|
|
42
|
+
const snapshot = await firestore.collection('hosts').doc(input.hostId).collection('functions').doc(input.id).get();
|
|
43
|
+
const data = snapshot.exists ? (_snapshot_data = snapshot.data()) != null ? _snapshot_data : {} : null;
|
|
44
|
+
if (!data || !live(data) || typeof data['name'] !== 'string') return null;
|
|
45
|
+
return _extends({
|
|
46
|
+
name: data['name'],
|
|
47
|
+
parameters: Array.isArray(data['parameters']) ? data['parameters'] : [],
|
|
48
|
+
variables: Array.isArray(data['variables']) ? data['variables'] : [],
|
|
49
|
+
operations: Array.isArray(data['operations']) ? data['operations'] : []
|
|
50
|
+
}, typeof data['returnValue'] === 'string' ? {
|
|
51
|
+
returnValue: data['returnValue']
|
|
52
|
+
} : {});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=ai-logic-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-logic-records.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 { HostFunction } from '@aglyn/aglyn/app-utils/functions'\nimport type { AiLogicSiteVariable } from '../model/ai-logic-job'\n\n/**\n * What a `logic` job reads (AGL-3603): the site's variables and function\n * names, in the window the Functions & Variables page itself reads, and one\n * saved function by id. Functions and variables are core documents\n * (`hosts/{hostId}/functions`, `hosts/{hostId}/variables`), read through the\n * Admin SDK as projections of the fields named here.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\ntype Data = Record<string, unknown>\n\n/** Records of one kind a job reads: the logic page's own ceiling. */\nexport const AI_LOGIC_RECORDS_WINDOW = 100\n\nconst live = (data: Data) => data['deletedAt'] == null\n\nexport interface AiLogicRecords {\n variables: AiLogicSiteVariable[]\n functions: string[]\n}\n\n/** The site's live variables (name, type, value) and function names. */\nexport async function readAiLogicRecords(firestore: Firestore, hostId: string): Promise<AiLogicRecords> {\n const host = firestore.collection('hosts').doc(hostId)\n const [variables, functions] = await Promise.all([\n host.collection('variables').select('name', 'type', 'value', 'deletedAt').limit(AI_LOGIC_RECORDS_WINDOW).get(),\n host.collection('functions').select('name', 'deletedAt').limit(AI_LOGIC_RECORDS_WINDOW).get(),\n ])\n return {\n variables: variables.docs\n .map((doc) => (doc.data() ?? {}) as Data)\n .filter(live)\n .filter((data) => typeof data['name'] === 'string' && data['name'])\n .map((data) => ({\n name: String(data['name']),\n type: (typeof data['type'] === 'string' ? data['type'] : 'text') as AiLogicSiteVariable['type'],\n value: typeof data['value'] === 'string' ? data['value'] : '',\n })),\n functions: functions.docs\n .map((doc) => (doc.data() ?? {}) as Data)\n .filter(live)\n .map((data) => (typeof data['name'] === 'string' ? data['name'].trim() : ''))\n .filter(Boolean),\n }\n}\n\n/** One saved function of the site, or `null` for one that does not exist or was deleted. */\nexport async function readAiLogicFunction(\n firestore: Firestore,\n input: { hostId: string; id: string },\n): Promise<HostFunction | null> {\n const snapshot = await firestore.collection('hosts').doc(input.hostId).collection('functions').doc(input.id).get()\n const data = snapshot.exists ? ((snapshot.data() ?? {}) as Data) : null\n if (!data || !live(data) || typeof data['name'] !== 'string') return null\n return {\n name: data['name'],\n parameters: Array.isArray(data['parameters']) ? (data['parameters'] as HostFunction['parameters']) : [],\n variables: Array.isArray(data['variables']) ? (data['variables'] as HostFunction['variables']) : [],\n operations: Array.isArray(data['operations']) ? (data['operations'] as HostFunction['operations']) : [],\n ...(typeof data['returnValue'] === 'string' ? { returnValue: data['returnValue'] } : {}),\n }\n}\n"],"names":["AI_LOGIC_RECORDS_WINDOW","live","data","readAiLogicRecords","firestore","hostId","host","collection","doc","variables","functions","Promise","all","select","limit","get","docs","map","filter","name","String","type","value","trim","Boolean","readAiLogicFunction","input","snapshot","id","exists","parameters","Array","isArray","operations","returnValue"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAgBD,mEAAmE,GACnE,OAAO,MAAMA,0BAA0B,IAAG;AAE1C,MAAMC,OAAO,CAACC,OAAeA,IAAI,CAAC,YAAY,IAAI;AAOlD,sEAAsE,GACtE,OAAO,eAAeC,mBAAmBC,SAAoB,EAAEC,MAAc;IAC3E,MAAMC,OAAOF,UAAUG,UAAU,CAAC,SAASC,GAAG,CAACH;IAC/C,MAAM,CAACI,WAAWC,UAAU,GAAG,MAAMC,QAAQC,GAAG,CAAC;QAC/CN,KAAKC,UAAU,CAAC,aAAaM,MAAM,CAAC,QAAQ,QAAQ,SAAS,aAAaC,KAAK,CAACd,yBAAyBe,GAAG;QAC5GT,KAAKC,UAAU,CAAC,aAAaM,MAAM,CAAC,QAAQ,aAAaC,KAAK,CAACd,yBAAyBe,GAAG;KAC5F;IACD,OAAO;QACLN,WAAWA,UAAUO,IAAI,CACtBC,GAAG,CAAC,CAACT;gBAASA;oBAAAA,YAAAA,IAAIN,IAAI,cAARM,YAAc,CAAC;WAC7BU,MAAM,CAACjB,MACPiB,MAAM,CAAC,CAAChB,OAAS,OAAOA,IAAI,CAAC,OAAO,KAAK,YAAYA,IAAI,CAAC,OAAO,EACjEe,GAAG,CAAC,CAACf,OAAU,CAAA;gBACdiB,MAAMC,OAAOlB,IAAI,CAAC,OAAO;gBACzBmB,MAAO,OAAOnB,IAAI,CAAC,OAAO,KAAK,WAAWA,IAAI,CAAC,OAAO,GAAG;gBACzDoB,OAAO,OAAOpB,IAAI,CAAC,QAAQ,KAAK,WAAWA,IAAI,CAAC,QAAQ,GAAG;YAC7D,CAAA;QACFQ,WAAWA,UAAUM,IAAI,CACtBC,GAAG,CAAC,CAACT;gBAASA;oBAAAA,YAAAA,IAAIN,IAAI,cAARM,YAAc,CAAC;WAC7BU,MAAM,CAACjB,MACPgB,GAAG,CAAC,CAACf,OAAU,OAAOA,IAAI,CAAC,OAAO,KAAK,WAAWA,IAAI,CAAC,OAAO,CAACqB,IAAI,KAAK,IACxEL,MAAM,CAACM;IACZ;AACF;AAEA,0FAA0F,GAC1F,OAAO,eAAeC,oBACpBrB,SAAoB,EACpBsB,KAAqC;QAGJC;IADjC,MAAMA,WAAW,MAAMvB,UAAUG,UAAU,CAAC,SAASC,GAAG,CAACkB,MAAMrB,MAAM,EAAEE,UAAU,CAAC,aAAaC,GAAG,CAACkB,MAAME,EAAE,EAAEb,GAAG;IAChH,MAAMb,OAAOyB,SAASE,MAAM,IAAKF,iBAAAA,SAASzB,IAAI,cAAbyB,iBAAmB,CAAC,IAAc;IACnE,IAAI,CAACzB,QAAQ,CAACD,KAAKC,SAAS,OAAOA,IAAI,CAAC,OAAO,KAAK,UAAU,OAAO;IACrE,OAAO;QACLiB,MAAMjB,IAAI,CAAC,OAAO;QAClB4B,YAAYC,MAAMC,OAAO,CAAC9B,IAAI,CAAC,aAAa,IAAKA,IAAI,CAAC,aAAa,GAAkC,EAAE;QACvGO,WAAWsB,MAAMC,OAAO,CAAC9B,IAAI,CAAC,YAAY,IAAKA,IAAI,CAAC,YAAY,GAAiC,EAAE;QACnG+B,YAAYF,MAAMC,OAAO,CAAC9B,IAAI,CAAC,aAAa,IAAKA,IAAI,CAAC,aAAa,GAAkC,EAAE;OACnG,OAAOA,IAAI,CAAC,cAAc,KAAK,WAAW;QAAEgC,aAAahC,IAAI,CAAC,cAAc;IAAC,IAAI,CAAC;AAE1F"}
|
|
@@ -52,6 +52,13 @@ export interface AiSitePublishDeps {
|
|
|
52
52
|
sendSitePublished?: typeof sendGa4SitePublished;
|
|
53
53
|
dropCache?: typeof dropPluginSiteCache;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether a build puts its pages live (AGL-3616): only where its request
|
|
57
|
+
* asked to (`publish`, set by Assist's proposal) AND the member ticked the
|
|
58
|
+
* plan card's box when confirming (`publishConfirmed`, set by the resume
|
|
59
|
+
* door). Every other build leaves drafts.
|
|
60
|
+
*/
|
|
61
|
+
export declare function aiJobPublishesBuild(job: Pick<AiJob, 'kind' | 'inputs'>): boolean;
|
|
55
62
|
/** Whether a finished job of this shape publishes what it built. */
|
|
56
63
|
export declare function aiJobPublishesSite(job: Pick<AiJob, 'kind' | 'inputs'>): boolean;
|
|
57
64
|
/**
|
|
@@ -22,6 +22,15 @@ import { decodeStoredNodes, encodeStoredNodes } from "@aglyn/aglyn/app-utils/sto
|
|
|
22
22
|
import { dropPluginSiteCache } from "@aglyn/aglyn/plugin-manager/plugin-site-cache";
|
|
23
23
|
import { sendGa4SitePublished } from "@aglyn/tenant-data-admin/server/ga4-measurement-protocol";
|
|
24
24
|
import { FieldValue } from "firebase-admin/firestore";
|
|
25
|
+
/**
|
|
26
|
+
* Whether a build puts its pages live (AGL-3616): only where its request
|
|
27
|
+
* asked to (`publish`, set by Assist's proposal) AND the member ticked the
|
|
28
|
+
* plan card's box when confirming (`publishConfirmed`, set by the resume
|
|
29
|
+
* door). Every other build leaves drafts.
|
|
30
|
+
*/ export function aiJobPublishesBuild(job) {
|
|
31
|
+
var _job_inputs, _job_inputs1;
|
|
32
|
+
return job.kind === 'build' && ((_job_inputs = job.inputs) == null ? void 0 : _job_inputs['publish']) === true && ((_job_inputs1 = job.inputs) == null ? void 0 : _job_inputs1['publishConfirmed']) === true;
|
|
33
|
+
}
|
|
25
34
|
/** Whether a finished job of this shape publishes what it built. */ export function aiJobPublishesSite(job) {
|
|
26
35
|
var _job_inputs;
|
|
27
36
|
return job.kind === 'site' && ((_job_inputs = job.inputs) == null ? void 0 : _job_inputs['autoConfirm']) === true;
|