@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-gate.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 { DecodedIdToken } from 'firebase-admin/auth'\nimport {\n checkEntitlement,\n type LockdownFeatureKey,\n type ReleaseFlagKey,\n} from '@aglyn/aglyn/server'\nimport {\n assistFreeTasteRefusalText,\n assistHardCapRefusalText,\n assistRefusedByHardCap,\n} from '@aglyn/aglyn/app-utils/assist-credits'\nimport type { AglynOrganization } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport type { OrgFeatureFlags } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n checkAiClientIpRateLimit,\n freeAccountAgeRefusal,\n} from './ai-abuse-guards'\nimport {\n checkRateLimit,\n rateLimitHeaders,\n type RateLimitResult,\n} from '@aglyn/tenant-data-admin/server/api-http'\nimport { aiOverageReservationRefusal } from '../billing/ai-overage-gate'\nimport { recordUserAiRefusal } from '../usage/ai-usage-by-user'\nimport { authForPool } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport {\n publicAssistQuota,\n reserveAssistMessage,\n type AssistReservation,\n} from '../usage/assist-usage'\nimport { aiAllotmentRefusalText } from '../model/ai-allotments'\nimport { aiUsageMeter } from '../usage/ai-usage-meter'\nimport {\n emailUnverifiedResponse,\n firebaseAdmin,\n isImpersonationSession,\n} from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { featureLockdownRefusal, lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown'\nimport {\n permissionRefusal,\n getOrgForUser,\n memberHasPermissionOnHost,\n} from '@aglyn/tenant-data-admin/server/organizations'\nimport { isServerReleaseFlagOnForOrg } from '@aglyn/tenant-data-admin/server/release-flags'\n\n/**\n * The gate ladder every AI door climbs before it spends a token (AGL-2903).\n *\n * The rungs, in order — each one a refusal the door sends as is, and each\n * composed from the helper the existing doors already call rather than a\n * second implementation of it:\n *\n * 405 not POST\n * 401 no bearer token, or a token the verifier refused\n * 403 email unverified (an impersonation session is exempt)\n * 400 the body did not NAME the org it is metered against (AGL-1934)\n * 403 the caller is not a member of that org\n * 403 the member's role lacks the door's AI permission (AGL-2927) — only\n * when the door names one; a verified staff claim passes, as it does\n * at every other org route\n * 404 the release flag is off — a released-off feature does not exist;\n * a verified staff claim previews through it\n * 403 the org's plan lacks the entitlement — a plan-less org resolves as\n * free, which is a real answer, never a loading default\n * 423 lockdown: the scope verdict (platform/org/user) and then the\n * feature kill switch, with its per-feature staff bypass\n * 429 the per-uid rate limit, which fails SOFT: it is a per-instance\n * window that smooths bursts and is not the spend bound\n * 429 the per-address rate limit (AGL-2925), the same window keyed on\n * the trusted-hop client address, so a farm rotating accounts\n * behind one address meets one budget; skipped when no address is\n * readable\n * 403 account age (AGL-2925): a Free workspace's caller must hold an\n * account older than `AI_FREE_MIN_ACCOUNT_AGE_HOURS`; paid\n * workspaces and staff never consult it, and a record that cannot\n * be read is a 503 rather than an admission\n * --- the reservation, which fails CLOSED: it is the only global, atomic\n * bound on provider spend, so a reservation that cannot be taken\n * refuses with 503 rather than calling the provider uncapped. On a\n * Free workspace it also decides the taste's own rungs — the\n * account's daily requests, its refusal pause, its monthly\n * allowance across every workspace it owns, and the platform-wide\n * daily ceiling — inside the same transaction\n * 429 a hard AI allotment (AGL-2942) — the caller's, the caller's on the\n * named site, or the site's — decided inside the same transaction\n * once the workspace's own ceilings admitted the request, so it sits\n * inside the band and never admits what the band refused\n *\n * A door that gets a context back has a reservation in hand and owes the\n * meter a record — or `releaseAssistMessage` if the provider was never\n * reached. What differs between doors is parameters: which entitlement,\n * which flag, which lockdown key, which rate window.\n *\n * The console chat route keeps its own copy of these rungs rather than\n * calling this: its retrieval-first design (AGL-2486) answers from the\n * docs BETWEEN the rate limit and the reservation, so its ladder is not a\n * prefix of this one, and it resolves the entitlement after the rate limit\n * because a free workspace is admitted in limited mode rather than refused.\n * The copy assistant differs the same way at the entitlement and carries no\n * flag by design. Both are documented at the route.\n */\nexport interface AiGateConfig {\n /** The plan entitlement the door sells under (`checkEntitlement`). */\n feature: keyof OrgFeatureFlags\n /** The release flag that makes the door exist. */\n releaseFlag: ReleaseFlagKey\n /** The feature kill switch on the staff lockdown page. */\n lockdownFeature: LockdownFeatureKey\n /**\n * The org permission the door sells under (AGL-2927): `ai.use` for the\n * assistants, `ai.generate` for a generation job. Resolved by\n * `memberHasPermissionOnHost` on the org axis or, for a site collaborator, on\n * the host the body named (`AiGateInput.hostId`). Omitted, the rung is\n * skipped — which is the right reading for a door that has not decided\n * which key it sells under, and the wrong one for any door a customer\n * can reach, so every shipped door names one.\n */\n permission?: 'ai.use' | 'ai.generate'\n rateLimit: {\n /** Prefix of the per-uid key, so two doors do not share one window. */\n key: string\n limit: number\n windowMs: number\n }\n}\n\nexport interface AiGateInput {\n request: Request\n /** The org the parsed body named; empty when it named none. */\n orgId: string\n /**\n * The site the parsed body named, when it named one. A collaborator's\n * permission is decided on this site; an org-wide member's is the same\n * everywhere, so a door on an org-level surface may leave it empty.\n */\n hostId?: string | null\n now?: Date\n}\n\n/** Everything a door needs after the ladder admitted the request. */\nexport interface AiGateContext {\n uid: string\n decoded: DecodedIdToken\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n firestore: FirebaseFirestore.Firestore\n rate: RateLimitResult\n reservation: AssistReservation\n}\n\nfunction bearerToken(request: Request): string | undefined {\n const authorization = request.headers.get('authorization') ?? ''\n return authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n}\n\n/**\n * Climb the ladder. Resolves to the `Response` to send when a rung\n * refuses, or to the resolved context when every rung admitted the request.\n */\nexport async function aiGateLadder(\n input: AiGateInput,\n config: AiGateConfig,\n): Promise<Response | AiGateContext> {\n const { request } = input\n if (request.method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n const idToken = bearerToken(request)\n if (!idToken) {\n return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n }\n\n const app = firebaseAdmin.app()\n let decoded: DecodedIdToken\n try {\n decoded = await app.auth().verifyIdToken(idToken)\n } catch (error) {\n // A refused credential is the caller's fault and a 401; anything else\n // — the verifier's own key endpoint unreachable — is ours, and it\n // propagates so the door answers 500 rather than telling every user\n // their credential is bad during an outage (AGL-1993).\n if (isRefusedIdToken(error)) {\n return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n }\n throw error\n }\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n const staff = decoded['staff'] === true\n\n const orgId = input.orgId.trim()\n if (!orgId) {\n return Response.json(\n { error: 'Open a workspace before using this feature' },\n { status: 400 },\n )\n }\n\n // Scoped to the NAMED org: membership of some other paid org is not a key\n // to this one, and the org this returns is the one every later rung and\n // the meter read.\n const resolved = await getOrgForUser(decoded.uid, orgId)\n if (!resolved || resolved.orgId !== orgId) {\n return Response.json(\n { error: 'You are not a member of that organization' },\n { status: 403 },\n )\n }\n const org = resolved.org ?? {}\n\n // Permission (AGL-2927), directly after membership: it is a fact about\n // the CALLER, so it is answered before anything about the workspace —\n // the plan, the lockdown state, the quota — is disclosed. Staff pass, as\n // they do at every other org route.\n if (\n config.permission &&\n !staff &&\n !(await memberHasPermissionOnHost(\n orgId,\n input.hostId,\n resolved.member,\n config.permission,\n ))\n ) {\n return permissionRefusal(config.permission)\n }\n\n // The flag closes the ROUTE, not just the UI (AGL-1653): a released-off\n // feature does not exist, so nothing below this line — not the plan, not\n // the lockdown state — is disclosed while it is off.\n if (!staff && !(await isServerReleaseFlagOnForOrg(config.releaseFlag, orgId))) {\n return Response.json({ error: 'Not found' }, { status: 404 })\n }\n\n if (!checkEntitlement(org, config.feature)) {\n return Response.json(\n {\n error: \"This workspace's plan does not include that feature\",\n reason: 'entitlement',\n },\n { status: 403 },\n )\n }\n\n const locked = await lockdownRefusal({\n request,\n staff,\n uid: decoded.uid,\n org: org as Record<string, unknown>,\n })\n if (locked) return locked\n const featureLocked = await featureLockdownRefusal({\n feature: config.lockdownFeature,\n staff,\n // The workspace-scoped pause on the same key (AGL-2927): the staff org\n // page's spend stop, which leaves the entitlement in place.\n orgId,\n })\n if (featureLocked) return featureLocked\n\n const rate = checkRateLimit(`${config.rateLimit.key}:${decoded.uid}`, {\n limit: config.rateLimit.limit,\n windowMs: config.rateLimit.windowMs,\n })\n if (!rate.allowed) {\n return Response.json(\n { error: 'Too many requests — slow down a moment', reason: 'rate' },\n { status: 429, headers: rateLimitHeaders(rate) },\n )\n }\n // The same window keyed on the address (AGL-2925): a farm rotating\n // fresh accounts behind one NAT meets one budget rather than one per\n // account. Answered with the same status and reason so the panel's\n // handling is unchanged.\n const ipRate = checkAiClientIpRateLimit(request.headers)\n if (ipRate && !ipRate.allowed) {\n return Response.json(\n { error: 'Too many requests — slow down a moment', reason: 'rate' },\n { status: 429, headers: rateLimitHeaders(ipRate) },\n )\n }\n\n // A Free workspace's caller must have held an account for a day\n // (AGL-2925). Read from the pool the token was minted in — a tenant\n // user's record is not in the project pool — and after the rate limits,\n // so a burst cannot turn one cached read into many.\n const tooYoung = await freeAccountAgeRefusal({\n uid: decoded.uid,\n org,\n staff,\n getUser: (uid) => authForPool(decoded.firebase?.tenant).getUser(uid),\n now: input.now,\n })\n if (tooYoung) return tooYoung\n\n // RESERVED, not merely checked (AGL-2057/2073): read-and-increment in one\n // transaction BEFORE a token is spent, so N concurrent requests cannot all\n // read \"under the cap\", and a caller that hangs up has been counted. The\n // org document is passed so the plan's own band binds, not only the\n // operator backstop.\n const firestore = app.firestore()\n let reservation: AssistReservation\n try {\n reservation = await reserveAssistMessage(\n firestore,\n orgId,\n true,\n input.now ?? new Date(),\n org,\n // Who is asking and on which site, for the allotments that apply.\n { uid: decoded.uid, hostId: input.hostId ?? null },\n )\n } catch (error) {\n console.error('ai reservation failed', error)\n return Response.json(\n { error: 'This feature is temporarily unavailable' },\n { status: 503 },\n )\n }\n // A refusal is the caller's as well as the workspace's (AGL-2928): the\n // per-person month counts it beside the org counter the reservation moved.\n recordUserAiRefusal(firestore, orgId, decoded.uid, reservation)\n if (!reservation.allowed) {\n const refusedBy = reservation.refusedBy\n // The usage strip's envelope (AGL-2942), on the refusal as on an answer,\n // so the strip shows why the request stopped without a read of its own.\n const meter = aiUsageMeter(reservation)\n // A hard allotment is a monthly line a manager drew inside the band: a\n // 429 like the message cap, because it resets on the calendar, with the\n // sentence naming who can raise it.\n if (refusedBy === 'allotment') {\n return Response.json(\n {\n error: aiAllotmentRefusalText(reservation.allotment?.refusal?.scope),\n reason: 'quota',\n refusedBy,\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: 429 },\n )\n }\n // AGLYN'S OWN OVERAGE GUARDS (AGL-3011), before the workspace's own\n // controls because they are the narrower answer: a refusal here already\n // knows WHICH limit stopped the request and whether the workspace can do\n // anything about it. Everything else falls through unchanged.\n const overage = aiOverageReservationRefusal(reservation)\n if (overage) {\n return Response.json(\n {\n error: overage.text,\n reason: 'quota',\n refusedBy,\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: overage.status },\n )\n }\n // The org's own wall is a 402 (AGL-2653): credits past the band are for\n // sale and this workspace switched the sale off, so the sentence names\n // the switch. A spend ceiling and a message cap keep the 429, and are\n // told apart in words because one resets on a clock and one does not.\n const hardCapped = assistRefusedByHardCap(org, refusedBy)\n return Response.json(\n {\n error: hardCapped\n ? assistHardCapRefusalText(org)\n : // The Free taste's own precautions (AGL-2925) have their own\n // sentences: each names a clock or an upgrade, never a figure.\n (assistFreeTasteRefusalText(refusedBy) ??\n (refusedBy === 'budget' || refusedBy === 'band'\n ? reservation.budgetUsd === null\n ? 'This workspace reached its AI spending limit for the month'\n : 'This workspace used its AI credits for the month'\n : 'This workspace reached its AI limit for the month')),\n reason: 'quota',\n // CREDITS, never the reservation itself — its figures are our\n // provider bill and do not leave the server.\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: hardCapped ? 402 : 429 },\n )\n }\n\n return {\n uid: decoded.uid,\n decoded,\n staff,\n orgId,\n org,\n firestore,\n rate,\n reservation,\n }\n}\n"],"names":["checkEntitlement","assistFreeTasteRefusalText","assistHardCapRefusalText","assistRefusedByHardCap","checkAiClientIpRateLimit","freeAccountAgeRefusal","checkRateLimit","rateLimitHeaders","aiOverageReservationRefusal","recordUserAiRefusal","authForPool","publicAssistQuota","reserveAssistMessage","aiAllotmentRefusalText","aiUsageMeter","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","isRefusedIdToken","featureLockdownRefusal","lockdownRefusal","permissionRefusal","getOrgForUser","memberHasPermissionOnHost","isServerReleaseFlagOnForOrg","bearerToken","request","authorization","headers","get","startsWith","slice","length","undefined","aiGateLadder","input","config","resolved","method","Response","json","error","status","idToken","app","decoded","auth","verifyIdToken","email_verified","staff","orgId","trim","uid","org","permission","hostId","member","releaseFlag","feature","reason","locked","featureLocked","lockdownFeature","rate","rateLimit","key","limit","windowMs","allowed","ipRate","tooYoung","getUser","firebase","tenant","now","firestore","reservation","Date","console","refusedBy","meter","allotment","refusal","scope","quota","overage","text","hardCapped","budgetUsd"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-gate.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 { DecodedIdToken } from 'firebase-admin/auth'\nimport {\n checkEntitlement,\n type LockdownFeatureKey,\n type ReleaseFlagKey,\n} from '@aglyn/aglyn/server'\nimport {\n assistFreeTasteRefusalText,\n assistHardCapRefusalText,\n assistRefusedByHardCap,\n} from '../usage/assist-credits'\nimport type { AglynOrganization } from '@aglyn/aglyn/foundation/definitions/organization.types'\nimport type { OrgFeatureFlags } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport {\n checkAiClientIpRateLimit,\n freeAccountAgeRefusal,\n} from './ai-abuse-guards'\nimport {\n checkRateLimit,\n rateLimitHeaders,\n type RateLimitResult,\n} from '@aglyn/tenant-data-admin/server/api-http'\nimport { aiOverageReservationRefusal } from '../billing/ai-overage-gate'\nimport { recordUserAiRefusal } from '../usage/ai-usage-by-user'\nimport { authForPool } from '@aglyn/tenant-data-admin/server/auth-pools'\nimport {\n publicAssistQuota,\n reserveAssistMessage,\n type AssistReservation,\n} from '../usage/assist-usage'\nimport { aiAllotmentRefusalText } from '../model/ai-allotments'\nimport { aiUsageMeter } from '../usage/ai-usage-meter'\nimport {\n emailUnverifiedResponse,\n firebaseAdmin,\n isImpersonationSession,\n} from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { isRefusedIdToken } from '@aglyn/tenant-data-admin/server/id-token-refusal'\nimport { featureLockdownRefusal, lockdownRefusal } from '@aglyn/tenant-data-admin/server/lockdown'\nimport {\n permissionRefusal,\n getOrgForUser,\n memberHasPermissionOnHost,\n} from '@aglyn/tenant-data-admin/server/organizations'\nimport { isServerReleaseFlagOnForOrg } from '@aglyn/tenant-data-admin/server/release-flags'\n\n/**\n * The gate ladder every AI door climbs before it spends a token (AGL-2903).\n *\n * The rungs, in order — each one a refusal the door sends as is, and each\n * composed from the helper the existing doors already call rather than a\n * second implementation of it:\n *\n * 405 not POST\n * 401 no bearer token, or a token the verifier refused\n * 403 email unverified (an impersonation session is exempt)\n * 400 the body did not NAME the org it is metered against (AGL-1934)\n * 403 the caller is not a member of that org\n * 403 the member's role lacks the door's AI permission (AGL-2927) — only\n * when the door names one; a verified staff claim passes, as it does\n * at every other org route\n * 404 the release flag is off — a released-off feature does not exist;\n * a verified staff claim previews through it\n * 403 the org's plan lacks the entitlement — a plan-less org resolves as\n * free, which is a real answer, never a loading default\n * 423 lockdown: the scope verdict (platform/org/user) and then the\n * feature kill switch, with its per-feature staff bypass\n * 429 the per-uid rate limit, which fails SOFT: it is a per-instance\n * window that smooths bursts and is not the spend bound\n * 429 the per-address rate limit (AGL-2925), the same window keyed on\n * the trusted-hop client address, so a farm rotating accounts\n * behind one address meets one budget; skipped when no address is\n * readable\n * 403 account age (AGL-2925): a Free workspace's caller must hold an\n * account older than `AI_FREE_MIN_ACCOUNT_AGE_HOURS`; paid\n * workspaces and staff never consult it, and a record that cannot\n * be read is a 503 rather than an admission\n * --- the reservation, which fails CLOSED: it is the only global, atomic\n * bound on provider spend, so a reservation that cannot be taken\n * refuses with 503 rather than calling the provider uncapped. On a\n * Free workspace it also decides the taste's own rungs — the\n * account's daily requests, its refusal pause, its monthly\n * allowance across every workspace it owns, and the platform-wide\n * daily ceiling — inside the same transaction\n * 429 a hard AI allotment (AGL-2942) — the caller's, the caller's on the\n * named site, or the site's — decided inside the same transaction\n * once the workspace's own ceilings admitted the request, so it sits\n * inside the band and never admits what the band refused\n *\n * A door that gets a context back has a reservation in hand and owes the\n * meter a record — or `releaseAssistMessage` if the provider was never\n * reached. What differs between doors is parameters: which entitlement,\n * which flag, which lockdown key, which rate window.\n *\n * The console chat route keeps its own copy of these rungs rather than\n * calling this: its retrieval-first design (AGL-2486) answers from the\n * docs BETWEEN the rate limit and the reservation, so its ladder is not a\n * prefix of this one, and it resolves the entitlement after the rate limit\n * because a free workspace is admitted in limited mode rather than refused.\n * The copy assistant differs the same way at the entitlement and carries no\n * flag by design. Both are documented at the route.\n */\nexport interface AiGateConfig {\n /** The plan entitlement the door sells under (`checkEntitlement`). */\n feature: keyof OrgFeatureFlags\n /** The release flag that makes the door exist. */\n releaseFlag: ReleaseFlagKey\n /** The feature kill switch on the staff lockdown page. */\n lockdownFeature: LockdownFeatureKey\n /**\n * The org permission the door sells under (AGL-2927): `ai.use` for the\n * assistants, `ai.generate` for a generation job. Resolved by\n * `memberHasPermissionOnHost` on the org axis or, for a site collaborator, on\n * the host the body named (`AiGateInput.hostId`). Omitted, the rung is\n * skipped — which is the right reading for a door that has not decided\n * which key it sells under, and the wrong one for any door a customer\n * can reach, so every shipped door names one.\n */\n permission?: 'ai.use' | 'ai.generate'\n rateLimit: {\n /** Prefix of the per-uid key, so two doors do not share one window. */\n key: string\n limit: number\n windowMs: number\n }\n}\n\nexport interface AiGateInput {\n request: Request\n /** The org the parsed body named; empty when it named none. */\n orgId: string\n /**\n * The site the parsed body named, when it named one. A collaborator's\n * permission is decided on this site; an org-wide member's is the same\n * everywhere, so a door on an org-level surface may leave it empty.\n */\n hostId?: string | null\n now?: Date\n}\n\n/** Everything a door needs after the ladder admitted the request. */\nexport interface AiGateContext {\n uid: string\n decoded: DecodedIdToken\n staff: boolean\n orgId: string\n org: Partial<AglynOrganization>\n firestore: FirebaseFirestore.Firestore\n rate: RateLimitResult\n reservation: AssistReservation\n}\n\nfunction bearerToken(request: Request): string | undefined {\n const authorization = request.headers.get('authorization') ?? ''\n return authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n}\n\n/**\n * Climb the ladder. Resolves to the `Response` to send when a rung\n * refuses, or to the resolved context when every rung admitted the request.\n */\nexport async function aiGateLadder(\n input: AiGateInput,\n config: AiGateConfig,\n): Promise<Response | AiGateContext> {\n const { request } = input\n if (request.method !== 'POST') {\n return Response.json({ error: 'Method not allowed' }, { status: 405 })\n }\n const idToken = bearerToken(request)\n if (!idToken) {\n return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n }\n\n const app = firebaseAdmin.app()\n let decoded: DecodedIdToken\n try {\n decoded = await app.auth().verifyIdToken(idToken)\n } catch (error) {\n // A refused credential is the caller's fault and a 401; anything else\n // — the verifier's own key endpoint unreachable — is ours, and it\n // propagates so the door answers 500 rather than telling every user\n // their credential is bad during an outage (AGL-1993).\n if (isRefusedIdToken(error)) {\n return Response.json({ error: 'Unauthenticated' }, { status: 401 })\n }\n throw error\n }\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n return emailUnverifiedResponse()\n }\n const staff = decoded['staff'] === true\n\n const orgId = input.orgId.trim()\n if (!orgId) {\n return Response.json(\n { error: 'Open a workspace before using this feature' },\n { status: 400 },\n )\n }\n\n // Scoped to the NAMED org: membership of some other paid org is not a key\n // to this one, and the org this returns is the one every later rung and\n // the meter read.\n const resolved = await getOrgForUser(decoded.uid, orgId)\n if (!resolved || resolved.orgId !== orgId) {\n return Response.json(\n { error: 'You are not a member of that organization' },\n { status: 403 },\n )\n }\n const org = resolved.org ?? {}\n\n // Permission (AGL-2927), directly after membership: it is a fact about\n // the CALLER, so it is answered before anything about the workspace —\n // the plan, the lockdown state, the quota — is disclosed. Staff pass, as\n // they do at every other org route.\n if (\n config.permission &&\n !staff &&\n !(await memberHasPermissionOnHost(\n orgId,\n input.hostId,\n resolved.member,\n config.permission,\n ))\n ) {\n return permissionRefusal(config.permission)\n }\n\n // The flag closes the ROUTE, not just the UI (AGL-1653): a released-off\n // feature does not exist, so nothing below this line — not the plan, not\n // the lockdown state — is disclosed while it is off.\n if (!staff && !(await isServerReleaseFlagOnForOrg(config.releaseFlag, orgId))) {\n return Response.json({ error: 'Not found' }, { status: 404 })\n }\n\n if (!checkEntitlement(org, config.feature)) {\n return Response.json(\n {\n error: \"This workspace's plan does not include that feature\",\n reason: 'entitlement',\n },\n { status: 403 },\n )\n }\n\n const locked = await lockdownRefusal({\n request,\n staff,\n uid: decoded.uid,\n org: org as Record<string, unknown>,\n })\n if (locked) return locked\n const featureLocked = await featureLockdownRefusal({\n feature: config.lockdownFeature,\n staff,\n // The workspace-scoped pause on the same key (AGL-2927): the staff org\n // page's spend stop, which leaves the entitlement in place.\n orgId,\n })\n if (featureLocked) return featureLocked\n\n const rate = checkRateLimit(`${config.rateLimit.key}:${decoded.uid}`, {\n limit: config.rateLimit.limit,\n windowMs: config.rateLimit.windowMs,\n })\n if (!rate.allowed) {\n return Response.json(\n { error: 'Too many requests — slow down a moment', reason: 'rate' },\n { status: 429, headers: rateLimitHeaders(rate) },\n )\n }\n // The same window keyed on the address (AGL-2925): a farm rotating\n // fresh accounts behind one NAT meets one budget rather than one per\n // account. Answered with the same status and reason so the panel's\n // handling is unchanged.\n const ipRate = checkAiClientIpRateLimit(request.headers)\n if (ipRate && !ipRate.allowed) {\n return Response.json(\n { error: 'Too many requests — slow down a moment', reason: 'rate' },\n { status: 429, headers: rateLimitHeaders(ipRate) },\n )\n }\n\n // A Free workspace's caller must have held an account for a day\n // (AGL-2925). Read from the pool the token was minted in — a tenant\n // user's record is not in the project pool — and after the rate limits,\n // so a burst cannot turn one cached read into many.\n const tooYoung = await freeAccountAgeRefusal({\n uid: decoded.uid,\n org,\n staff,\n getUser: (uid) => authForPool(decoded.firebase?.tenant).getUser(uid),\n now: input.now,\n })\n if (tooYoung) return tooYoung\n\n // RESERVED, not merely checked (AGL-2057/2073): read-and-increment in one\n // transaction BEFORE a token is spent, so N concurrent requests cannot all\n // read \"under the cap\", and a caller that hangs up has been counted. The\n // org document is passed so the plan's own band binds, not only the\n // operator backstop.\n const firestore = app.firestore()\n let reservation: AssistReservation\n try {\n reservation = await reserveAssistMessage(\n firestore,\n orgId,\n true,\n input.now ?? new Date(),\n org,\n // Who is asking and on which site, for the allotments that apply.\n { uid: decoded.uid, hostId: input.hostId ?? null },\n )\n } catch (error) {\n console.error('ai reservation failed', error)\n return Response.json(\n { error: 'This feature is temporarily unavailable' },\n { status: 503 },\n )\n }\n // A refusal is the caller's as well as the workspace's (AGL-2928): the\n // per-person month counts it beside the org counter the reservation moved.\n recordUserAiRefusal(firestore, orgId, decoded.uid, reservation)\n if (!reservation.allowed) {\n const refusedBy = reservation.refusedBy\n // The usage strip's envelope (AGL-2942), on the refusal as on an answer,\n // so the strip shows why the request stopped without a read of its own.\n const meter = aiUsageMeter(reservation)\n // A hard allotment is a monthly line a manager drew inside the band: a\n // 429 like the message cap, because it resets on the calendar, with the\n // sentence naming who can raise it.\n if (refusedBy === 'allotment') {\n return Response.json(\n {\n error: aiAllotmentRefusalText(reservation.allotment?.refusal?.scope),\n reason: 'quota',\n refusedBy,\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: 429 },\n )\n }\n // AGLYN'S OWN OVERAGE GUARDS (AGL-3011), before the workspace's own\n // controls because they are the narrower answer: a refusal here already\n // knows WHICH limit stopped the request and whether the workspace can do\n // anything about it. Everything else falls through unchanged.\n const overage = aiOverageReservationRefusal(reservation)\n if (overage) {\n return Response.json(\n {\n error: overage.text,\n reason: 'quota',\n refusedBy,\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: overage.status },\n )\n }\n // The org's own wall is a 402 (AGL-2653): credits past the band are for\n // sale and this workspace switched the sale off, so the sentence names\n // the switch. A spend ceiling and a message cap keep the 429, and are\n // told apart in words because one resets on a clock and one does not.\n const hardCapped = assistRefusedByHardCap(org, refusedBy)\n return Response.json(\n {\n error: hardCapped\n ? assistHardCapRefusalText(org)\n : // The Free taste's own precautions (AGL-2925) have their own\n // sentences: each names a clock or an upgrade, never a figure.\n (assistFreeTasteRefusalText(refusedBy) ??\n (refusedBy === 'budget' || refusedBy === 'band'\n ? reservation.budgetUsd === null\n ? 'This workspace reached its AI spending limit for the month'\n : 'This workspace used its AI credits for the month'\n : 'This workspace reached its AI limit for the month')),\n reason: 'quota',\n // CREDITS, never the reservation itself — its figures are our\n // provider bill and do not leave the server.\n quota: publicAssistQuota(reservation),\n meter,\n },\n { status: hardCapped ? 402 : 429 },\n )\n }\n\n return {\n uid: decoded.uid,\n decoded,\n staff,\n orgId,\n org,\n firestore,\n rate,\n reservation,\n }\n}\n"],"names":["checkEntitlement","assistFreeTasteRefusalText","assistHardCapRefusalText","assistRefusedByHardCap","checkAiClientIpRateLimit","freeAccountAgeRefusal","checkRateLimit","rateLimitHeaders","aiOverageReservationRefusal","recordUserAiRefusal","authForPool","publicAssistQuota","reserveAssistMessage","aiAllotmentRefusalText","aiUsageMeter","emailUnverifiedResponse","firebaseAdmin","isImpersonationSession","isRefusedIdToken","featureLockdownRefusal","lockdownRefusal","permissionRefusal","getOrgForUser","memberHasPermissionOnHost","isServerReleaseFlagOnForOrg","bearerToken","request","authorization","headers","get","startsWith","slice","length","undefined","aiGateLadder","input","config","resolved","method","Response","json","error","status","idToken","app","decoded","auth","verifyIdToken","email_verified","staff","orgId","trim","uid","org","permission","hostId","member","releaseFlag","feature","reason","locked","featureLocked","lockdownFeature","rate","rateLimit","key","limit","windowMs","allowed","ipRate","tooYoung","getUser","firebase","tenant","now","firestore","reservation","Date","console","refusedBy","meter","allotment","refusal","scope","quota","overage","text","hardCapped","budgetUsd"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ import { createIdUrlSafe } from "@aglyn/aglyn/foundation/constants/app";
|
|
|
24
24
|
import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
|
|
25
25
|
import { NodeType } from "@aglyn/aglyn/types/nodes";
|
|
26
26
|
import { AI_COMPONENT_VISIBILITY_PROPS, isAiComponentPropToken } from "./ai-component-bindings.js";
|
|
27
|
-
import {
|
|
27
|
+
import { sanitizePortableDefinition } from "@aglyn/aglyn/app-utils/node-definition-sanitizer";
|
|
28
28
|
/**
|
|
29
29
|
* `sx` keys a model may set: spacing, color, typography, layout, borders,
|
|
30
30
|
* display, flex and grid. Fixed rather than derived — MUI's system accepts
|
|
@@ -308,7 +308,7 @@ function textLimitFor(entry, name, props) {
|
|
|
308
308
|
case 'screen':
|
|
309
309
|
{
|
|
310
310
|
if (screenIds ? !screenIds.has(value) : !SCREEN_ID.test(value)) {
|
|
311
|
-
repairs.push(`${nodeId}.${name} names a
|
|
311
|
+
repairs.push(`${nodeId}.${name} names a page this site does not have; dropped`);
|
|
312
312
|
return undefined;
|
|
313
313
|
}
|
|
314
314
|
break;
|
|
@@ -750,7 +750,7 @@ function validate(input, surface, context) {
|
|
|
750
750
|
...definition.allow,
|
|
751
751
|
REUSABLE_INSTANCE_COMPONENT_ID
|
|
752
752
|
] : definition.allow;
|
|
753
|
-
const sanitized =
|
|
753
|
+
const sanitized = sanitizePortableDefinition({
|
|
754
754
|
rootId,
|
|
755
755
|
nodes: forSanitizer
|
|
756
756
|
}, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-node-tree.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 {\n AI_TEXT_LIMITS,\n isHeadlineVariant,\n nodeCapabilityProps,\n type AiPaletteEntry,\n type AiPropSchema,\n type AiSurface,\n} from './ai-palette'\nimport {\n AI_NODE_CAPABILITIES,\n AI_PALETTE,\n AI_SURFACES,\n AI_SX_TOKENS,\n} from './ai-palette.generated'\nimport { linealRelationshipPermits } from '@aglyn/aglyn/app-utils/lineal-order'\nimport {\n NODE_MAP_MAX_BYTES,\n nodeMapBytes,\n} from '@aglyn/aglyn/app-utils/measure-node-map'\nimport { parseMediaRef } from '@aglyn/aglyn/app-utils/media-ref'\nimport {\n COMPONENT_PROP_NAME_PATTERN,\n REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { createIdUrlSafe } from '@aglyn/aglyn/foundation/constants/app'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport { NodeType, type NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiComponentPropTypes } from '../model/ai-site-inventory'\nimport {\n AI_COMPONENT_VISIBILITY_PROPS,\n isAiComponentPropToken,\n} from './ai-component-bindings'\n\nimport { sanitizeMarketplaceDefinition } from '@aglyn/aglyn/app-utils/node-definition-sanitizer'\n\n/**\n * The one path an AI-emitted node tree takes into storage (AGL-2905).\n *\n * A model answers with a flat node map in the shape the canvas persists.\n * Nothing about that map is trusted: the component ids, the parent/child\n * pairs, every prop and every `sx` key are what the model chose to write,\n * and the renderer spreads props onto the DOM. So the tree is first held to\n * the same sanitizer a marketplace install passes — reachability from the\n * root, the surface's component allowlist, the persisted node keys, `href`\n * and `src` protocols, a size cap — and then to what a listing never needed\n * checking because a human authored it in the editor:\n *\n * - the parent/child restrictions the besigner enforces on drop\n * (`restrictChildren` / `restrictParent`), and whether an element can hold\n * children at all;\n * - every prop against the generated palette's schema — unknown props are\n * dropped, enum values coerced case-insensitively, a required prop that is\n * missing refuses the tree;\n * - `sx` against a fixed key set and a value grammar of theme tokens, numbers\n * and CSS lengths or keywords, with nothing that can reach a URL or a\n * stylesheet;\n * - copy length by role — a headline, a paragraph, a button label;\n * - links to a screen the host has, a root-relative path or an `https:` URL,\n * and media to a DAM reference or an `https:` URL;\n * - the stored-node-map byte ceiling, measured the way the save measures it.\n *\n * ## The site's own records, when the caller names them (AGL-2935)\n *\n * A reusable-component instance, a form picked on the Forms page and a\n * dataset are each \"a reference into another document\", which is why the\n * palette leaves them off: from a description alone a model can only invent\n * an id. A generator that has read the site's inventory can name real ones,\n * and the building doctrine requires it to — a repeat is placed as\n * instances of one component, and a form is bound by id rather than drawn\n * inline. So the three are admitted exactly as screen links are: only\n * against the ids the caller supplies. With no `componentIds` an instance is\n * still refused; with no `formIds` or `datasetIds` the binding is dropped.\n *\n * What comes out is the flat `NodesMap` `encodeStoredNodes` takes, with every\n * id minted fresh the way a preset graft mints them, so nothing the model\n * wrote can collide with a node already on the canvas. `sourceIds` maps each\n * minted id back to the id the model wrote, so a caller can quote a refused\n * subtree in the model's own words. Every drop is listed in `repairs`; every\n * refusal names its `code`. The function never throws.\n */\n\nexport type AiNodeTreeRefusalCode =\n /** Not an object with a `rootId` and a `nodes` map, or an unknown surface. */\n | 'invalid-input'\n /** The root node carries a component the surface does not start with. */\n | 'root'\n /** A structural failure the sanitizer named: missing node, empty tree. */\n | 'structure'\n /** A component id outside the surface's allowlist. */\n | 'component'\n /** A parent/child pair the besigner would refuse on drop. */\n | 'lineage'\n /** A prop the palette marks required is absent. */\n | 'required-prop'\n /** Over the sanitizer's or the stored-node-map's byte ceiling. */\n | 'too-large'\n /** A component instance naming a component the site does not have. */\n | 'reference'\n\nexport type AiNodeTreeResult =\n | {\n ok: true\n rootId: string\n nodes: NodesMap\n repairs: string[]\n /** Each minted id → the id the model wrote for that node. */\n sourceIds: Record<string, string>\n }\n | { ok: false; error: string; code: AiNodeTreeRefusalCode }\n\nexport interface AiNodeTreeContext {\n /** Screen ids a `screen` prop may name. Absent: any well-formed id. */\n screenIds?: Iterable<string>\n /** DAM media ids a `media` prop may reference. Absent: any well-formed reference. */\n assetIds?: Iterable<string>\n /**\n * Reusable components an instance may place. Absent: no instance is\n * admitted, which is the palette's own rule for a tree nobody grounded.\n */\n componentIds?: Iterable<string>\n /** Declared props per component id, which an instance's values are held to. */\n componentProps?: Record<string, AiComponentPropTypes>\n /** Forms on the Forms page a Form element may bind by `formId`. */\n formIds?: Iterable<string>\n /** Datasets a Form element may write to by `datasetId`. */\n datasetIds?: Iterable<string>\n /**\n * Binding tokens a `url` or `media` prop may hold whole — an Image whose\n * `src` is `{{entry.coverImage}}` on a page template, filled per record\n * when the page renders (AGL-2909). Absent: a token in either role is\n * dropped like any other value that is not an address.\n */\n bindingTokens?: Iterable<string>\n /**\n * The tree defines a reusable component (AGL-2908). A field that is not\n * copy — a switch, a dropdown, a screen, an address, a picture, a number —\n * and the `hideIf` and `hideUnless` directives may then hold one of the\n * component's own `{{prop.<name>}}` tokens as their whole value, and a\n * placed component may be handed one in its `propValues`; each is kept as\n * written. Which property a token names, and whether its kind binds to that\n * field, is the component step's check (`ai-component-bindings.ts`).\n * Absent: such a token is dropped like any value the field cannot hold.\n */\n definesComponent?: boolean\n}\n\n/**\n * `sx` keys a model may set: spacing, color, typography, layout, borders,\n * display, flex and grid. Fixed rather than derived — MUI's system accepts\n * far more (pseudo-selectors, nested selectors, arbitrary CSS properties)\n * and every one of those is a way to reach outside the element.\n */\nexport const AI_SX_ALLOWED_KEYS: ReadonlySet<string> = new Set([\n // spacing\n 'm',\n 'mt',\n 'mr',\n 'mb',\n 'ml',\n 'mx',\n 'my',\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'p',\n 'pt',\n 'pr',\n 'pb',\n 'pl',\n 'px',\n 'py',\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'gap',\n 'rowGap',\n 'columnGap',\n // color\n 'color',\n 'bgcolor',\n 'backgroundColor',\n 'borderColor',\n 'opacity',\n // typography\n 'typography',\n 'fontSize',\n 'fontWeight',\n 'fontFamily',\n 'fontStyle',\n 'lineHeight',\n 'letterSpacing',\n 'textAlign',\n 'textTransform',\n 'textDecoration',\n 'whiteSpace',\n 'textOverflow',\n 'overflowWrap',\n 'wordBreak',\n // layout\n 'width',\n 'minWidth',\n 'maxWidth',\n 'height',\n 'minHeight',\n 'maxHeight',\n 'boxSizing',\n 'overflow',\n 'overflowX',\n 'overflowY',\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'aspectRatio',\n 'objectFit',\n // borders\n 'border',\n 'borderTop',\n 'borderRight',\n 'borderBottom',\n 'borderLeft',\n 'borderWidth',\n 'borderStyle',\n 'borderRadius',\n 'boxShadow',\n // display\n 'display',\n 'visibility',\n // flex\n 'flexDirection',\n 'flexWrap',\n 'justifyContent',\n 'alignItems',\n 'alignContent',\n 'alignSelf',\n 'justifySelf',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'flexBasis',\n 'order',\n // grid\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'gridColumn',\n 'gridRow',\n 'gridArea',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n 'justifyItems',\n 'placeItems',\n 'placeContent',\n])\n\n/**\n * Substrings no string a model wrote may carry, in a prop or an `sx` value.\n * Exported for the theme tool's component style values (AGL-2938), which are\n * the same kind of CSS value written into a theme instead of a node.\n */\nexport const HOSTILE_TEXT =\n /<\\s*\\/?\\s*(script|iframe|object|embed|svg|style|link|meta|base|frame|form|input|img)\\b|javascript:|vbscript:|data:text\\/html|expression\\s*\\(|url\\s*\\(|@import|!important|\\bon[a-z]+\\s*=/i\n\n/**\n * A CSS value a model may write into `sx`: lengths, numbers, keywords,\n * shorthands (`1px solid`), functions with plain arguments (`repeat(3, 1fr)`,\n * `rgba(0, 0, 0, 0.2)`), hex colors and theme tokens. No quotes, no\n * semicolons, no braces, no angle brackets, no colons — which is what keeps\n * a `url(`, a selector or a second declaration out.\n */\nexport const SX_VALUE = /^[A-Za-z0-9#.,%()\\s\\-_/+*]{1,100}$/\n\n/**\n * Node fields the sanitizer strips that a model has no business writing —\n * each is a way to reach past the node's own box, and each is named in\n * `repairs` so a route can tell the model what it lost.\n */\nconst STRIPPED_NODE_KEYS = [\n 'className',\n 'interactions',\n 'styleOverrides',\n 'attrOverrides',\n 'presetRef',\n] as const\n\nconst SCREEN_ID = /^[A-Za-z0-9_-]{1,64}$/\n/** The field an icon is picked with; its value carries a drawing no model can write. */\nconst ICON_PICKER_FIELD = 'icon-picker'\nconst MAX_SX_KEYS = 40\n/** A string prop with no role of its own — a color, a CSS length, an icon id. */\nconst PLAIN_STRING_MAX = 500\n\nconst paletteTokens = new Set<string>(AI_SX_TOKENS.palette)\nconst breakpoints = new Set<string>(AI_SX_TOKENS.breakpoints)\nconst typographyVariants = new Set<string>(AI_SX_TOKENS.typographyVariants)\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** An `https:` address that parses as one. */\nexport function isHttpsUrl(value: string): boolean {\n if (!/^https:\\/\\//i.test(value)) return false\n try {\n return new URL(value).protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/** A path on this site: one leading slash, never a scheme-relative `//`. */\nexport function isRootRelativePath(value: string): boolean {\n return /^\\/(?!\\/)[^\\s<>\"'`\\\\]*$/.test(value)\n}\n\nfunction sxValueAllowed(key: string, value: unknown): boolean {\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'string') return false\n const trimmed = value.trim()\n if (!trimmed || HOSTILE_TEXT.test(trimmed)) return false\n if (paletteTokens.has(trimmed)) return true\n if (key === 'typography' && typographyVariants.has(trimmed)) return true\n return SX_VALUE.test(trimmed)\n}\n\n/**\n * The allowed slice of an `sx` object, with a line per key or value dropped.\n * Responsive objects keep only breakpoint keys; anything deeper — a\n * pseudo-selector, a nested selector, an array — is not a value at all.\n */\nfunction sanitizeSx(\n nodeId: string,\n sx: unknown,\n repairs: string[],\n): Record<string, unknown> | undefined {\n if (!isRecord(sx)) {\n if (sx !== undefined)\n repairs.push(`${nodeId}: sx is not an object; dropped`)\n return undefined\n }\n const out: Record<string, unknown> = {}\n let kept = 0\n for (const [key, value] of Object.entries(sx)) {\n if (!AI_SX_ALLOWED_KEYS.has(key)) {\n repairs.push(`${nodeId}: sx.${key} is not an allowed style; dropped`)\n continue\n }\n if (kept >= MAX_SX_KEYS) {\n repairs.push(\n `${nodeId}: sx.${key} is past the ${MAX_SX_KEYS}-key ceiling; dropped`,\n )\n continue\n }\n if (isRecord(value)) {\n const responsive: Record<string, unknown> = {}\n for (const [breakpoint, inner] of Object.entries(value)) {\n if (!breakpoints.has(breakpoint)) {\n repairs.push(\n `${nodeId}: sx.${key}.${breakpoint} is not a breakpoint; dropped`,\n )\n continue\n }\n if (!sxValueAllowed(key, inner)) {\n repairs.push(\n `${nodeId}: sx.${key}.${breakpoint} is not an allowed value; dropped`,\n )\n continue\n }\n responsive[breakpoint] =\n typeof inner === 'string' ? inner.trim() : inner\n }\n if (Object.keys(responsive).length) {\n out[key] = responsive\n kept += 1\n }\n continue\n }\n if (!sxValueAllowed(key, value)) {\n repairs.push(`${nodeId}: sx.${key} is not an allowed value; dropped`)\n continue\n }\n out[key] = typeof value === 'string' ? value.trim() : value\n kept += 1\n }\n return kept ? out : undefined\n}\n\nfunction coerceEnum(schema: AiPropSchema, value: unknown): string | undefined {\n if (typeof value !== 'string' || !schema.enum) return undefined\n const wanted = value.trim().toLowerCase()\n return schema.enum.find((option) => option.toLowerCase() === wanted)\n}\n\nfunction coerceBoolean(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value\n if (value === 'true') return true\n if (value === 'false') return false\n return undefined\n}\n\nfunction coerceNumber(\n schema: AiPropSchema,\n value: unknown,\n): number | undefined {\n const parsed =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.trim() !== ''\n ? Number(value)\n : NaN\n if (!Number.isFinite(parsed)) return undefined\n if (schema.type === 'integer' && !Number.isInteger(parsed)) return undefined\n if (schema.minimum !== undefined && parsed < schema.minimum) return undefined\n if (schema.maximum !== undefined && parsed > schema.maximum) return undefined\n return parsed\n}\n\nfunction textLimitFor(\n entry: AiPaletteEntry,\n name: string,\n props: Record<string, unknown>,\n): number {\n const declared = entry.textLimits[name]\n // A heading's copy is held to the headline ceiling, decided by the variant\n // the same node carries; anything else keeps the ceiling the palette\n // assigned to the prop.\n if (name === 'children' && isHeadlineVariant(props.variant)) {\n return Math.min(declared ?? AI_TEXT_LIMITS.body, AI_TEXT_LIMITS.headline)\n }\n return declared ?? AI_TEXT_LIMITS.body\n}\n\n/**\n * One string prop against its role. Returns the value to store, or\n * `undefined` with the reason pushed to `repairs`. A binding token the\n * caller admitted passes a `url` or `media` role whole.\n */\nfunction sanitizeString(\n nodeId: string,\n entry: AiPaletteEntry,\n name: string,\n schema: AiPropSchema,\n raw: string,\n props: Record<string, unknown>,\n screenIds: Set<string> | null,\n assetIds: Set<string> | null,\n repairs: string[],\n bindingTokens: ReadonlySet<string> | null = null,\n): string | undefined {\n let value = raw.trim()\n if (HOSTILE_TEXT.test(value)) {\n repairs.push(`${nodeId}.${name} carried markup or script; dropped`)\n return undefined\n }\n // An icon is drawn from the path its picker stores beside the id, which a\n // model can never supply, so an icon a model names draws nothing where the\n // page is published (AGL-3054). The site owner picks it.\n if (entry.propFields[name] === ICON_PICKER_FIELD) {\n repairs.push(`${nodeId}.${name} is an icon, which the site owner picks from the library; dropped`)\n return undefined\n }\n const role = entry.propRoles[name]\n switch (role) {\n case 'text': {\n const limit = textLimitFor(entry, name, props)\n if (value.length > limit) {\n value = value.slice(0, limit).trimEnd()\n repairs.push(\n `${nodeId}.${name} was over ${limit} characters; truncated`,\n )\n }\n break\n }\n case 'url': {\n if (bindingTokens?.has(value)) break\n if (!isHttpsUrl(value) && !isRootRelativePath(value)) {\n repairs.push(\n `${nodeId}.${name} is neither an https: URL nor a path on this site; dropped`,\n )\n return undefined\n }\n break\n }\n case 'screen': {\n if (screenIds ? !screenIds.has(value) : !SCREEN_ID.test(value)) {\n repairs.push(\n `${nodeId}.${name} names a screen this site does not have; dropped`,\n )\n return undefined\n }\n break\n }\n case 'media': {\n if (bindingTokens?.has(value)) break\n if (isHttpsUrl(value)) break\n const ref = parseMediaRef(value)\n if (!ref || (assetIds && !assetIds.has(ref.mediaId))) {\n repairs.push(\n `${nodeId}.${name} is neither a media reference this site holds nor an https: URL; dropped`,\n )\n return undefined\n }\n break\n }\n default: {\n if (value.length > PLAIN_STRING_MAX) {\n repairs.push(\n `${nodeId}.${name} is over ${PLAIN_STRING_MAX} characters; dropped`,\n )\n return undefined\n }\n }\n }\n if (schema.pattern && !new RegExp(schema.pattern).test(value)) {\n repairs.push(`${nodeId}.${name} does not match its pattern; dropped`)\n return undefined\n }\n if (schema.minLength !== undefined && value.length < schema.minLength) {\n repairs.push(\n `${nodeId}.${name} is shorter than ${schema.minLength} characters; dropped`,\n )\n return undefined\n }\n if (schema.maxLength !== undefined && value.length > schema.maxLength) {\n value = value.slice(0, schema.maxLength).trimEnd()\n repairs.push(\n `${nodeId}.${name} was over ${schema.maxLength} characters; truncated`,\n )\n }\n return value\n}\n\n/**\n * The element's own declarations plus the node capabilities every element\n * carries (AGL-3156) — repeating, today. Two shapes only, so they are built\n * once each rather than per node: a capability prop is the same on every\n * element with the same child contract.\n *\n * The element wins a name collision. A component that declared a prop of its\n * own under a capability's name would be describing its own attribute, and the\n * validator has to hold it to that.\n */\nconst CAPABLE_ENTRIES = new WeakMap<AiPaletteEntry, AiPaletteEntry>()\n\nfunction withNodeCapabilities(entry: AiPaletteEntry): AiPaletteEntry {\n const cached = CAPABLE_ENTRIES.get(entry)\n if (cached) return cached\n const capability = nodeCapabilityProps(\n AI_NODE_CAPABILITIES,\n entry.acceptsChildren,\n )\n const merged: AiPaletteEntry = {\n ...entry,\n propsSchema: {\n ...entry.propsSchema,\n properties: {\n ...capability.properties,\n ...entry.propsSchema.properties,\n },\n },\n propRoles: { ...capability.propRoles, ...entry.propRoles },\n textLimits: { ...capability.textLimits, ...entry.textLimits },\n }\n CAPABLE_ENTRIES.set(entry, merged)\n return merged\n}\n\nfunction sanitizeProps(\n nodeId: string,\n paletteEntry: AiPaletteEntry,\n raw: unknown,\n screenIds: Set<string> | null,\n assetIds: Set<string> | null,\n repairs: string[],\n bindingTokens: ReadonlySet<string> | null = null,\n): { props: Record<string, unknown> } | { missing: string } {\n const entry = withNodeCapabilities(paletteEntry)\n const source = isRecord(raw) ? raw : {}\n const props: Record<string, unknown> = {}\n const declared = entry.propsSchema.properties\n for (const [name, value] of Object.entries(source)) {\n // `sx` is read off the node instead; a model that put it here meant the\n // same thing, and the caller has already moved it.\n if (name === 'sx' || value === undefined || value === null) continue\n const schema = declared[name]\n if (!schema) {\n repairs.push(\n `${nodeId}.${name} is not a prop of ${entry.displayName}; dropped`,\n )\n continue\n }\n if (schema.enum) {\n const coerced = coerceEnum(schema, value)\n if (coerced === undefined) {\n repairs.push(\n `${nodeId}.${name} is not one of ${schema.enum.join('|')}; dropped`,\n )\n continue\n }\n if (coerced !== value) {\n repairs.push(\n `${nodeId}.${name} was spelled \"${String(value)}\"; normalized to \"${coerced}\"`,\n )\n }\n props[name] = coerced\n continue\n }\n switch (schema.type) {\n case 'boolean': {\n const coerced = coerceBoolean(value)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.${name} is not a boolean; dropped`)\n continue\n }\n props[name] = coerced\n break\n }\n case 'number':\n case 'integer': {\n const coerced = coerceNumber(schema, value)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.${name} is not a number in range; dropped`)\n continue\n }\n props[name] = coerced\n break\n }\n default: {\n const text =\n typeof value === 'string'\n ? value\n : typeof value === 'number' || typeof value === 'boolean'\n ? String(value)\n : undefined\n if (text === undefined) {\n repairs.push(`${nodeId}.${name} is not text; dropped`)\n continue\n }\n const cleaned = sanitizeString(\n nodeId,\n entry,\n name,\n schema,\n text,\n source,\n screenIds,\n assetIds,\n repairs,\n bindingTokens,\n )\n if (cleaned === undefined) continue\n props[name] = cleaned\n }\n }\n }\n for (const name of entry.propsSchema.required) {\n if (props[name] === undefined) return { missing: name }\n }\n return { props }\n}\n\n/** Surfaces that compose page elements, and so may place a component instance. */\nconst INSTANCE_SURFACES: ReadonlySet<AiSurface> = new Set([\n 'screen',\n 'layout',\n 'component',\n])\n\nconst FORM_COMPONENT_ID = 'form'\n\n/** The instance prop naming the component it places; stored on every instance node. */\nexport const AI_INSTANCE_REF_PROP = 'refId'\n\ninterface ReferenceSets {\n componentIds: Set<string> | null\n componentProps: Record<string, AiComponentPropTypes>\n formIds: Set<string> | null\n datasetIds: Set<string> | null\n assetIds: Set<string> | null\n /** The tree defines a reusable component, which may hand its own property tokens on. */\n definesComponent: boolean\n}\n\n/**\n * One value an instance fills in for a declared prop, held to what the\n * declaration's type can hold: a picture from the library, a link, a number,\n * a switch, or copy.\n */\nfunction instancePropValue(\n nodeId: string,\n name: string,\n type: string,\n raw: unknown,\n assetIds: Set<string> | null,\n repairs: string[],\n): unknown {\n if (type === 'number') {\n const parsed = typeof raw === 'number' ? raw : Number(raw)\n if (raw === '' || !Number.isFinite(parsed)) {\n repairs.push(`${nodeId}.propValues.${name} is not a number; dropped`)\n return undefined\n }\n return parsed\n }\n if (type === 'boolean') {\n const coerced = coerceBoolean(raw)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.propValues.${name} is not a boolean; dropped`)\n }\n return coerced\n }\n if (type === 'icon') {\n // A page cannot fill an icon: the pick is an id and the path drawn from\n // it, and a word in its place draws nothing (AGL-3054). The property is\n // left for the site owner to pick.\n repairs.push(`${nodeId}.propValues.${name} is an icon, which the site owner picks from the library; dropped`)\n return undefined\n }\n if (typeof raw !== 'string' && typeof raw !== 'number' && typeof raw !== 'boolean') {\n repairs.push(`${nodeId}.propValues.${name} is not a value; dropped`)\n return undefined\n }\n const value = String(raw).trim()\n if (HOSTILE_TEXT.test(value)) {\n repairs.push(`${nodeId}.propValues.${name} carried markup or script; dropped`)\n return undefined\n }\n if (type === 'image') {\n if (isHttpsUrl(value)) return value\n const ref = parseMediaRef(value)\n if (!ref || (assetIds && !assetIds.has(ref.mediaId))) {\n repairs.push(\n `${nodeId}.propValues.${name} is neither a media reference this site holds nor an https: URL; dropped`,\n )\n return undefined\n }\n return value\n }\n if (type === 'href') {\n if (isHttpsUrl(value) || isRootRelativePath(value)) return value\n repairs.push(\n `${nodeId}.propValues.${name} is neither an https: URL nor a path on this site; dropped`,\n )\n return undefined\n }\n if (value.length > AI_TEXT_LIMITS.body) {\n repairs.push(\n `${nodeId}.propValues.${name} was over ${AI_TEXT_LIMITS.body} characters; truncated`,\n )\n return value.slice(0, AI_TEXT_LIMITS.body).trimEnd()\n }\n return value\n}\n\n/**\n * The site-record props of one node — an instance's `refId` and\n * `propValues`, a Form's `formId` and `datasetId` — checked against the ids\n * the caller supplied. They leave `props` before the palette pass, which\n * would drop them as undeclared, and come back in `kept` once checked.\n */\nfunction sanitizeReferenceProps(\n nodeId: string,\n componentId: string,\n props: Record<string, unknown>,\n refs: ReferenceSets,\n repairs: string[],\n): { props: Record<string, unknown>; kept: Record<string, unknown> } | { refusal: string } {\n const rest: Record<string, unknown> = { ...props }\n const kept: Record<string, unknown> = {}\n if (componentId === REUSABLE_INSTANCE_COMPONENT_ID) {\n const refId = typeof rest[AI_INSTANCE_REF_PROP] === 'string' ? String(rest[AI_INSTANCE_REF_PROP]).trim() : ''\n delete rest[AI_INSTANCE_REF_PROP]\n if (!refId || !refs.componentIds?.has(refId)) {\n return {\n refusal: `Reusable Component (${nodeId}) names a component this site does not have`,\n }\n }\n kept[AI_INSTANCE_REF_PROP] = refId\n const rawValues = rest[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n delete rest[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n if (rawValues !== undefined && !isRecord(rawValues)) {\n repairs.push(`${nodeId}.propValues is not an object; dropped`)\n }\n const declared = refs.componentProps[refId]\n const values: Record<string, unknown> = {}\n for (const [name, raw] of Object.entries(isRecord(rawValues) ? rawValues : {})) {\n if (raw === undefined || raw === null) continue\n if (!COMPONENT_PROP_NAME_PATTERN.test(name)) {\n repairs.push(`${nodeId}.propValues.${name} is not a prop name; dropped`)\n continue\n }\n if (declared && !(name in declared)) {\n repairs.push(`${nodeId}.propValues.${name} is not a prop of that component; dropped`)\n continue\n }\n if (refs.definesComponent && isAiComponentPropToken(raw)) {\n // A property of the component being defined, handed on whole; the\n // component step's check holds it to the kind this prop declares.\n values[name] = raw.trim()\n continue\n }\n const value = instancePropValue(\n nodeId,\n name,\n declared?.[name] ?? 'text',\n raw,\n refs.assetIds,\n repairs,\n )\n if (value !== undefined) values[name] = value\n }\n if (Object.keys(values).length) kept[REUSABLE_INSTANCE_PROP_VALUES_KEY] = values\n return { props: rest, kept }\n }\n if (componentId === FORM_COMPONENT_ID) {\n const bindings: Array<[prop: string, ids: Set<string> | null, record: string]> = [\n ['formId', refs.formIds, 'form'],\n ['datasetId', refs.datasetIds, 'dataset'],\n ]\n for (const [prop, ids, record] of bindings) {\n const raw = rest[prop]\n delete rest[prop]\n if (raw === undefined || raw === null || raw === '') continue\n const id = typeof raw === 'string' ? raw.trim() : ''\n if (id && ids?.has(id)) {\n kept[prop] = id\n } else {\n repairs.push(`${nodeId}.${prop} names a ${record} this site does not have; dropped`)\n }\n }\n }\n return { props: rest, kept }\n}\n\nfunction refusalCode(error: string): AiNodeTreeRefusalCode {\n if (/too large/i.test(error)) return 'too-large'\n if (/cannot be published/i.test(error)) return 'component'\n return 'structure'\n}\n\n/**\n * Whether the root the model wrote is the surface's root: the canvas wrapper\n * for a document surface (which the sanitizer exempts by the same shape), or\n * the named component otherwise.\n */\nfunction rootMatches(surfaceRoot: string, componentId: unknown): boolean {\n if (surfaceRoot === 'div') {\n return componentId == null || componentId === '' || componentId === 'div'\n }\n return componentId === surfaceRoot\n}\n\nexport function validateAiNodeTree(\n input: unknown,\n surface: AiSurface,\n context?: AiNodeTreeContext,\n): AiNodeTreeResult {\n try {\n return validate(input, surface, context)\n } catch (error) {\n return {\n ok: false,\n code: 'invalid-input',\n error: `The tree could not be read: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n}\n\nexport type AiNodePatchResult =\n | {\n ok: true\n props: Record<string, unknown>\n sx: Record<string, unknown> | undefined\n repairs: string[]\n }\n | { ok: false; error: string; code: AiNodeTreeRefusalCode }\n\n/**\n * One element's prop and style patch, held to the rules `validateAiNodeTree`\n * holds an element it places — for an edit to an element already on the\n * canvas, where there is no tree to validate.\n *\n * The same passes run: unknown props dropped, enums coerced, every string\n * held to its role and screened for markup, `sx` held to the key set and the\n * value grammar. The one difference is `required`: a patch is merged over the\n * props the element already has, so a required prop the patch does not name\n * is not missing.\n */\nexport function validateAiNodePatch(\n componentId: string,\n patch: { props?: unknown; sx?: unknown },\n context?: AiNodeTreeContext,\n): AiNodePatchResult {\n const entry = AI_PALETTE[componentId]\n if (!entry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${componentId}\" is not in the palette`,\n }\n }\n const repairs: string[] = []\n const props = sanitizeProps(\n componentId,\n { ...entry, propsSchema: { ...entry.propsSchema, required: [] } },\n patch.props,\n context?.screenIds ? new Set(context.screenIds) : null,\n context?.assetIds ? new Set(context.assetIds) : null,\n repairs,\n )\n const sx =\n patch.sx === undefined ? undefined : sanitizeSx(componentId, patch.sx, repairs)\n return {\n ok: true,\n props: 'props' in props ? props.props : {},\n sx,\n repairs,\n }\n}\n\nfunction validate(\n input: unknown,\n surface: AiSurface,\n context: AiNodeTreeContext | undefined,\n): AiNodeTreeResult {\n const definition = AI_SURFACES[surface]\n if (!definition) {\n return {\n ok: false,\n code: 'invalid-input',\n error: `Unknown surface \"${surface}\"`,\n }\n }\n if (\n !isRecord(input) ||\n typeof input.rootId !== 'string' ||\n !isRecord(input.nodes)\n ) {\n return {\n ok: false,\n code: 'invalid-input',\n error: 'Expected an object with a string rootId and a nodes map',\n }\n }\n const rootId = input.rootId\n const nodes = input.nodes as Record<string, unknown>\n const root = nodes[rootId]\n if (!isRecord(root)) {\n return {\n ok: false,\n code: 'structure',\n error: 'Definition has no root node',\n }\n }\n if (!rootMatches(definition.root, root.componentId)) {\n return {\n ok: false,\n code: 'root',\n error: `A ${surface} starts with ${definition.root === 'div' ? 'the document wrapper' : `\"${definition.root}\"`}, not \"${String(root.componentId)}\"`,\n }\n }\n\n // The sanitizer keeps only the persisted node keys, and `sx` is not one of\n // them for a listing. Read it off every node first — from the node field\n // or from `props.sx`, which a model writes just as readily — and put it\n // back, checked, on the nodes that survive.\n const sxByNodeId = new Map<string, unknown>()\n const hiddenByNodeId = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecord(node)) continue\n const sx =\n node.sx !== undefined\n ? node.sx\n : isRecord(node.props)\n ? node.props.sx\n : undefined\n if (sx !== undefined) sxByNodeId.set(id, sx)\n if (node.hidden === true) hiddenByNodeId.add(id)\n }\n\n // A DAM reference (a `media:` value) is not a URL the sanitizer's\n // `src` policy admits — a listing must not carry another tenant's asset —\n // but on the tenant's own page it is exactly what an image should name.\n // Lift those values out before sanitizing and hand them back to the prop\n // pass, which holds them to the asset set the caller supplied.\n // A binding token the caller admitted (AGL-2909) is lifted the same way:\n // `{{entry.coverImage}}` is no address the sanitizer knows, and the prop\n // pass holds it to the tokens the caller named.\n const bindingTokens = context?.bindingTokens ? new Set(context.bindingTokens) : null\n // A reusable component's own property tokens (AGL-2908) are lifted too, and\n // kept as written rather than handed back to the prop pass: `{{prop.variant}}`\n // is no value a dropdown, a switch, an address or a visibility directive\n // admits. Copy keeps its tokens without lifting, as the text they are.\n const definesComponent = context?.definesComponent === true\n const mediaByNodeId = new Map<string, Record<string, string>>()\n const boundByNodeId = new Map<string, Record<string, string>>()\n const forSanitizer: Record<string, unknown> = {}\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecord(node) || !isRecord(node.props)) {\n forSanitizer[id] = node\n continue\n }\n const entry = AI_PALETTE[String(node.componentId)]\n const roles = entry?.propRoles ?? {}\n const props: Record<string, unknown> = { ...node.props }\n const lifted: Record<string, string> = {}\n const bound: Record<string, string> = {}\n for (const [key, value] of Object.entries(props)) {\n if (typeof value !== 'string') continue\n const role = roles[key]\n if (\n definesComponent &&\n isAiComponentPropToken(value) &&\n (AI_COMPONENT_VISIBILITY_PROPS.includes(key) ||\n (entry?.propsSchema.properties[key] !== undefined && role !== 'text'))\n ) {\n bound[key] = value.trim()\n delete props[key]\n continue\n }\n if (\n (role === 'media' && parseMediaRef(value.trim())) ||\n ((role === 'media' || role === 'url') && bindingTokens?.has(value.trim()))\n ) {\n lifted[key] = value\n delete props[key]\n }\n }\n if (Object.keys(lifted).length) mediaByNodeId.set(id, lifted)\n if (Object.keys(bound).length) boundByNodeId.set(id, bound)\n forSanitizer[id] = { ...node, props }\n }\n\n const refs: ReferenceSets = {\n componentIds: context?.componentIds ? new Set(context.componentIds) : null,\n componentProps: context?.componentProps ?? {},\n formIds: context?.formIds ? new Set(context.formIds) : null,\n datasetIds: context?.datasetIds ? new Set(context.datasetIds) : null,\n assetIds: context?.assetIds ? new Set(context.assetIds) : null,\n definesComponent,\n }\n // An instance is admitted only where the caller grounded it in the site's\n // components; the reference itself is checked on the node below.\n const allow =\n refs.componentIds && INSTANCE_SURFACES.has(surface)\n ? [...definition.allow, REUSABLE_INSTANCE_COMPONENT_ID]\n : definition.allow\n const sanitized = sanitizeMarketplaceDefinition(\n { rootId, nodes: forSanitizer },\n { componentIds: allow },\n )\n if (sanitized.ok === false) {\n return {\n ok: false,\n code: refusalCode(sanitized.error),\n error: sanitized.error,\n }\n }\n\n const screenIds = context?.screenIds ? new Set(context.screenIds) : null\n const assetIds = refs.assetIds\n const repairs: string[] = []\n const output: NodesMap = {}\n const rootIsWrapper =\n rootMatches('div', root.componentId) && definition.root === 'div'\n const minted = new Map<string, string>()\n const mint = (id: string): string => {\n const fresh =\n id === rootId && rootIsWrapper\n ? CANVAS_ROOT_ELEMENT_ID\n : createIdUrlSafe()\n minted.set(id, fresh)\n return fresh\n }\n\n /** Each visited node's parent by the model's own ids, for the parentId check. */\n const parentOf = new Map<string, string>()\n const queue: Array<{ id: string; parentId: string | null }> = [\n { id: rootId, parentId: null },\n ]\n mint(rootId)\n while (queue.length) {\n const { id, parentId } = queue.shift() as {\n id: string\n parentId: string | null\n }\n const node = sanitized.nodes[id]\n const entry = AI_PALETTE[node.componentId]\n if (!entry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${node.componentId}\" is not in the palette`,\n }\n }\n const newId = minted.get(id) as string\n const raw = nodes[id]\n if (isRecord(raw)) {\n // The sanitizer drops silently; the model is told what went.\n for (const key of STRIPPED_NODE_KEYS) {\n if (raw[key] !== undefined) {\n repairs.push(\n `${id}.${key} is not something a generated node may set; dropped`,\n )\n }\n }\n if (raw.hidden !== undefined && typeof raw.hidden !== 'boolean') {\n repairs.push(`${id}.hidden is not a boolean; dropped`)\n }\n if (raw.nodes !== undefined && !Array.isArray(raw.nodes)) {\n repairs.push(\n `${id}.nodes is not a list of child ids; its children were dropped`,\n )\n }\n if (\n raw.parentId !== undefined &&\n raw.parentId !== null &&\n raw.parentId !== (parentId === null ? null : parentOf.get(id))\n ) {\n repairs.push(\n `${id}.parentId disagreed with where the tree places it; recomputed`,\n )\n }\n if (isRecord(raw.props)) {\n const lifted = mediaByNodeId.get(id) ?? {}\n const kept = boundByNodeId.get(id) ?? {}\n for (const key of Object.keys(raw.props)) {\n if (\n key !== 'sx' &&\n lifted[key] === undefined &&\n kept[key] === undefined &&\n node.props?.[key] === undefined &&\n raw.props[key] !== undefined\n ) {\n repairs.push(\n `${id}.${key} was an unsafe handler, address or markup; dropped`,\n )\n }\n }\n }\n }\n const children: string[] = []\n for (const childId of node.nodes ?? []) {\n if (minted.has(childId)) {\n repairs.push(\n `${childId} was listed under more than one parent; the later listing was dropped`,\n )\n continue\n }\n const child = sanitized.nodes[childId]\n if (!child) continue\n const childEntry = AI_PALETTE[child.componentId]\n if (!childEntry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${child.componentId}\" is not in the palette`,\n }\n }\n if (!entry.acceptsChildren) {\n return {\n ok: false,\n code: 'lineage',\n error: `${entry.displayName} (${id}) cannot hold other elements, but lists ${childId}`,\n }\n }\n if (\n !linealRelationshipPermits(\n {\n componentId: child.componentId,\n pluginId: childEntry.pluginId,\n restrictParent: childEntry.restrictParent,\n },\n {\n componentId: node.componentId,\n pluginId: entry.pluginId,\n restrictChildren: entry.restrictChildren,\n },\n )\n ) {\n return {\n ok: false,\n code: 'lineage',\n error: `${childEntry.displayName} (${childId}) cannot be placed inside ${entry.displayName} (${id})`,\n }\n }\n children.push(mint(childId))\n parentOf.set(childId, id)\n queue.push({ id: childId, parentId: newId })\n }\n\n const reference = sanitizeReferenceProps(\n id,\n node.componentId,\n { ...node.props, ...mediaByNodeId.get(id) },\n refs,\n repairs,\n )\n if ('refusal' in reference) {\n return { ok: false, code: 'reference', error: reference.refusal }\n }\n // A required prop the component binds is present: its value is the token.\n const boundProps = boundByNodeId.get(id)\n const propsResult = sanitizeProps(\n id,\n boundProps\n ? {\n ...entry,\n propsSchema: {\n ...entry.propsSchema,\n required: entry.propsSchema.required.filter((name) => boundProps[name] === undefined),\n },\n }\n : entry,\n reference.props,\n screenIds,\n assetIds,\n repairs,\n bindingTokens,\n )\n if ('missing' in propsResult) {\n return {\n ok: false,\n code: 'required-prop',\n error: `${entry.displayName} (${id}) is missing its required \"${propsResult.missing}\" prop`,\n }\n }\n const sx = sanitizeSx(id, sxByNodeId.get(id), repairs)\n output[newId] = {\n $id: newId,\n type: NodeType.NODE,\n componentId: node.componentId,\n pluginId: entry.pluginId,\n parentId,\n nodes: children,\n props: { ...propsResult.props, ...reference.kept, ...boundProps },\n ...(sx ? { sx } : {}),\n ...(hiddenByNodeId.has(id) ? { hidden: true } : {}),\n }\n }\n\n // The save's own measurement (msgpack), not a JSON string length: the\n // ceiling is stated in those bytes, and a UTF-16 length reads multi-byte\n // copy as smaller than the document the save will refuse.\n const bytes = nodeMapBytes(output)\n if (bytes > NODE_MAP_MAX_BYTES) {\n return {\n ok: false,\n code: 'too-large',\n error: `The tree is ${bytes} bytes; the stored ceiling is ${NODE_MAP_MAX_BYTES}`,\n }\n }\n const sourceIds: Record<string, string> = {}\n for (const [source, fresh] of minted) sourceIds[fresh] = source\n return {\n ok: true,\n rootId: minted.get(rootId) as string,\n nodes: output,\n repairs,\n sourceIds,\n }\n}\n"],"names":["AI_TEXT_LIMITS","isHeadlineVariant","nodeCapabilityProps","AI_NODE_CAPABILITIES","AI_PALETTE","AI_SURFACES","AI_SX_TOKENS","linealRelationshipPermits","NODE_MAP_MAX_BYTES","nodeMapBytes","parseMediaRef","COMPONENT_PROP_NAME_PATTERN","REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","createIdUrlSafe","CANVAS_ROOT_ELEMENT_ID","NodeType","AI_COMPONENT_VISIBILITY_PROPS","isAiComponentPropToken","sanitizeMarketplaceDefinition","AI_SX_ALLOWED_KEYS","Set","HOSTILE_TEXT","SX_VALUE","STRIPPED_NODE_KEYS","SCREEN_ID","ICON_PICKER_FIELD","MAX_SX_KEYS","PLAIN_STRING_MAX","paletteTokens","palette","breakpoints","typographyVariants","isRecord","value","Array","isArray","isHttpsUrl","test","URL","protocol","isRootRelativePath","sxValueAllowed","key","Number","isFinite","trimmed","trim","has","sanitizeSx","nodeId","sx","repairs","undefined","push","out","kept","Object","entries","responsive","breakpoint","inner","keys","length","coerceEnum","schema","enum","wanted","toLowerCase","find","option","coerceBoolean","coerceNumber","parsed","NaN","type","isInteger","minimum","maximum","textLimitFor","entry","name","props","declared","textLimits","variant","Math","min","body","headline","sanitizeString","raw","screenIds","assetIds","bindingTokens","propFields","role","propRoles","limit","slice","trimEnd","ref","mediaId","pattern","RegExp","minLength","maxLength","CAPABLE_ENTRIES","WeakMap","withNodeCapabilities","cached","get","capability","acceptsChildren","merged","propsSchema","properties","set","sanitizeProps","paletteEntry","source","displayName","coerced","join","String","text","cleaned","required","missing","INSTANCE_SURFACES","FORM_COMPONENT_ID","AI_INSTANCE_REF_PROP","instancePropValue","sanitizeReferenceProps","componentId","refs","rest","refId","componentIds","refusal","rawValues","componentProps","values","definesComponent","bindings","formIds","datasetIds","prop","ids","record","id","refusalCode","error","rootMatches","surfaceRoot","validateAiNodeTree","input","surface","context","validate","ok","code","Error","message","validateAiNodePatch","patch","definition","rootId","nodes","root","sxByNodeId","Map","hiddenByNodeId","node","hidden","add","mediaByNodeId","boundByNodeId","forSanitizer","roles","lifted","bound","includes","allow","sanitized","output","rootIsWrapper","minted","mint","fresh","parentOf","queue","parentId","shift","newId","children","childId","child","childEntry","pluginId","restrictParent","restrictChildren","reference","boundProps","propsResult","filter","$id","NODE","bytes","sourceIds"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,cAAc,EACdC,iBAAiB,EACjBC,mBAAmB,QAId,kBAAc;AACrB,SACEC,oBAAoB,EACpBC,UAAU,EACVC,WAAW,EACXC,YAAY,QACP,4BAAwB;AAC/B,SAASC,yBAAyB,QAAQ,sCAAqC;AAC/E,SACEC,kBAAkB,EAClBC,YAAY,QACP,0CAAyC;AAChD,SAASC,aAAa,QAAQ,mCAAkC;AAChE,SACEC,2BAA2B,EAC3BC,8BAA8B,EAC9BC,iCAAiC,QAC5B,iDAAgD;AACvD,SAASC,eAAe,QAAQ,wCAAuC;AACvE,SAASC,sBAAsB,QAAQ,2CAA0C;AACjF,SAASC,QAAQ,QAAuB,2BAA0B;AAElE,SACEC,6BAA6B,EAC7BC,sBAAsB,QACjB,6BAAyB;AAEhC,SAASC,6BAA6B,QAAQ,mDAAkD;AAiHhG;;;;;CAKC,GACD,OAAO,MAAMC,qBAA0C,IAAIC,IAAI;IAC7D,UAAU;IACV;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,QAAQ;IACR;IACA;IACA;IACA;IACA;IACA,aAAa;IACb;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,SAAS;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,UAAU;IACV;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,UAAU;IACV;IACA;IACA,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,EAAC;AAEF;;;;CAIC,GACD,OAAO,MAAMC,eACX,2LAA0L;AAE5L;;;;;;CAMC,GACD,OAAO,MAAMC,WAAW,qCAAoC;AAE5D;;;;C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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-node-tree.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 {\n AI_TEXT_LIMITS,\n isHeadlineVariant,\n nodeCapabilityProps,\n type AiPaletteEntry,\n type AiPropSchema,\n type AiSurface,\n} from './ai-palette'\nimport {\n AI_NODE_CAPABILITIES,\n AI_PALETTE,\n AI_SURFACES,\n AI_SX_TOKENS,\n} from './ai-palette.generated'\nimport { linealRelationshipPermits } from '@aglyn/aglyn/app-utils/lineal-order'\nimport {\n NODE_MAP_MAX_BYTES,\n nodeMapBytes,\n} from '@aglyn/aglyn/app-utils/measure-node-map'\nimport { parseMediaRef } from '@aglyn/aglyn/app-utils/media-ref'\nimport {\n COMPONENT_PROP_NAME_PATTERN,\n REUSABLE_INSTANCE_COMPONENT_ID,\n REUSABLE_INSTANCE_PROP_VALUES_KEY,\n} from '@aglyn/aglyn/app-utils/reusable-component-keys'\nimport { createIdUrlSafe } from '@aglyn/aglyn/foundation/constants/app'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\nimport { NodeType, type NodesMap } from '@aglyn/aglyn/types/nodes'\nimport type { AiComponentPropTypes } from '../model/ai-site-inventory'\nimport {\n AI_COMPONENT_VISIBILITY_PROPS,\n isAiComponentPropToken,\n} from './ai-component-bindings'\n\nimport { sanitizePortableDefinition } from '@aglyn/aglyn/app-utils/node-definition-sanitizer'\n\n/**\n * The one path an AI-emitted node tree takes into storage (AGL-2905).\n *\n * A model answers with a flat node map in the shape the canvas persists.\n * Nothing about that map is trusted: the component ids, the parent/child\n * pairs, every prop and every `sx` key are what the model chose to write,\n * and the renderer spreads props onto the DOM. So the tree is first held to\n * the same sanitizer a marketplace install passes — reachability from the\n * root, the surface's component allowlist, the persisted node keys, `href`\n * and `src` protocols, a size cap — and then to what a listing never needed\n * checking because a human authored it in the editor:\n *\n * - the parent/child restrictions the besigner enforces on drop\n * (`restrictChildren` / `restrictParent`), and whether an element can hold\n * children at all;\n * - every prop against the generated palette's schema — unknown props are\n * dropped, enum values coerced case-insensitively, a required prop that is\n * missing refuses the tree;\n * - `sx` against a fixed key set and a value grammar of theme tokens, numbers\n * and CSS lengths or keywords, with nothing that can reach a URL or a\n * stylesheet;\n * - copy length by role — a headline, a paragraph, a button label;\n * - links to a screen the host has, a root-relative path or an `https:` URL,\n * and media to a DAM reference or an `https:` URL;\n * - the stored-node-map byte ceiling, measured the way the save measures it.\n *\n * ## The site's own records, when the caller names them (AGL-2935)\n *\n * A reusable-component instance, a form picked on the Forms page and a\n * dataset are each \"a reference into another document\", which is why the\n * palette leaves them off: from a description alone a model can only invent\n * an id. A generator that has read the site's inventory can name real ones,\n * and the building doctrine requires it to — a repeat is placed as\n * instances of one component, and a form is bound by id rather than drawn\n * inline. So the three are admitted exactly as screen links are: only\n * against the ids the caller supplies. With no `componentIds` an instance is\n * still refused; with no `formIds` or `datasetIds` the binding is dropped.\n *\n * What comes out is the flat `NodesMap` `encodeStoredNodes` takes, with every\n * id minted fresh the way a preset graft mints them, so nothing the model\n * wrote can collide with a node already on the canvas. `sourceIds` maps each\n * minted id back to the id the model wrote, so a caller can quote a refused\n * subtree in the model's own words. Every drop is listed in `repairs`; every\n * refusal names its `code`. The function never throws.\n */\n\nexport type AiNodeTreeRefusalCode =\n /** Not an object with a `rootId` and a `nodes` map, or an unknown surface. */\n | 'invalid-input'\n /** The root node carries a component the surface does not start with. */\n | 'root'\n /** A structural failure the sanitizer named: missing node, empty tree. */\n | 'structure'\n /** A component id outside the surface's allowlist. */\n | 'component'\n /** A parent/child pair the besigner would refuse on drop. */\n | 'lineage'\n /** A prop the palette marks required is absent. */\n | 'required-prop'\n /** Over the sanitizer's or the stored-node-map's byte ceiling. */\n | 'too-large'\n /** A component instance naming a component the site does not have. */\n | 'reference'\n\nexport type AiNodeTreeResult =\n | {\n ok: true\n rootId: string\n nodes: NodesMap\n repairs: string[]\n /** Each minted id → the id the model wrote for that node. */\n sourceIds: Record<string, string>\n }\n | { ok: false; error: string; code: AiNodeTreeRefusalCode }\n\nexport interface AiNodeTreeContext {\n /** Screen ids a `screen` prop may name. Absent: any well-formed id. */\n screenIds?: Iterable<string>\n /** DAM media ids a `media` prop may reference. Absent: any well-formed reference. */\n assetIds?: Iterable<string>\n /**\n * Reusable components an instance may place. Absent: no instance is\n * admitted, which is the palette's own rule for a tree nobody grounded.\n */\n componentIds?: Iterable<string>\n /** Declared props per component id, which an instance's values are held to. */\n componentProps?: Record<string, AiComponentPropTypes>\n /** Forms on the Forms page a Form element may bind by `formId`. */\n formIds?: Iterable<string>\n /** Datasets a Form element may write to by `datasetId`. */\n datasetIds?: Iterable<string>\n /**\n * Binding tokens a `url` or `media` prop may hold whole — an Image whose\n * `src` is `{{entry.coverImage}}` on a page template, filled per record\n * when the page renders (AGL-2909). Absent: a token in either role is\n * dropped like any other value that is not an address.\n */\n bindingTokens?: Iterable<string>\n /**\n * The tree defines a reusable component (AGL-2908). A field that is not\n * copy — a switch, a dropdown, a screen, an address, a picture, a number —\n * and the `hideIf` and `hideUnless` directives may then hold one of the\n * component's own `{{prop.<name>}}` tokens as their whole value, and a\n * placed component may be handed one in its `propValues`; each is kept as\n * written. Which property a token names, and whether its kind binds to that\n * field, is the component step's check (`ai-component-bindings.ts`).\n * Absent: such a token is dropped like any value the field cannot hold.\n */\n definesComponent?: boolean\n}\n\n/**\n * `sx` keys a model may set: spacing, color, typography, layout, borders,\n * display, flex and grid. Fixed rather than derived — MUI's system accepts\n * far more (pseudo-selectors, nested selectors, arbitrary CSS properties)\n * and every one of those is a way to reach outside the element.\n */\nexport const AI_SX_ALLOWED_KEYS: ReadonlySet<string> = new Set([\n // spacing\n 'm',\n 'mt',\n 'mr',\n 'mb',\n 'ml',\n 'mx',\n 'my',\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'p',\n 'pt',\n 'pr',\n 'pb',\n 'pl',\n 'px',\n 'py',\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'gap',\n 'rowGap',\n 'columnGap',\n // color\n 'color',\n 'bgcolor',\n 'backgroundColor',\n 'borderColor',\n 'opacity',\n // typography\n 'typography',\n 'fontSize',\n 'fontWeight',\n 'fontFamily',\n 'fontStyle',\n 'lineHeight',\n 'letterSpacing',\n 'textAlign',\n 'textTransform',\n 'textDecoration',\n 'whiteSpace',\n 'textOverflow',\n 'overflowWrap',\n 'wordBreak',\n // layout\n 'width',\n 'minWidth',\n 'maxWidth',\n 'height',\n 'minHeight',\n 'maxHeight',\n 'boxSizing',\n 'overflow',\n 'overflowX',\n 'overflowY',\n 'position',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'aspectRatio',\n 'objectFit',\n // borders\n 'border',\n 'borderTop',\n 'borderRight',\n 'borderBottom',\n 'borderLeft',\n 'borderWidth',\n 'borderStyle',\n 'borderRadius',\n 'boxShadow',\n // display\n 'display',\n 'visibility',\n // flex\n 'flexDirection',\n 'flexWrap',\n 'justifyContent',\n 'alignItems',\n 'alignContent',\n 'alignSelf',\n 'justifySelf',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'flexBasis',\n 'order',\n // grid\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'gridColumn',\n 'gridRow',\n 'gridArea',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n 'justifyItems',\n 'placeItems',\n 'placeContent',\n])\n\n/**\n * Substrings no string a model wrote may carry, in a prop or an `sx` value.\n * Exported for the theme tool's component style values (AGL-2938), which are\n * the same kind of CSS value written into a theme instead of a node.\n */\nexport const HOSTILE_TEXT =\n /<\\s*\\/?\\s*(script|iframe|object|embed|svg|style|link|meta|base|frame|form|input|img)\\b|javascript:|vbscript:|data:text\\/html|expression\\s*\\(|url\\s*\\(|@import|!important|\\bon[a-z]+\\s*=/i\n\n/**\n * A CSS value a model may write into `sx`: lengths, numbers, keywords,\n * shorthands (`1px solid`), functions with plain arguments (`repeat(3, 1fr)`,\n * `rgba(0, 0, 0, 0.2)`), hex colors and theme tokens. No quotes, no\n * semicolons, no braces, no angle brackets, no colons — which is what keeps\n * a `url(`, a selector or a second declaration out.\n */\nexport const SX_VALUE = /^[A-Za-z0-9#.,%()\\s\\-_/+*]{1,100}$/\n\n/**\n * Node fields the sanitizer strips that a model has no business writing —\n * each is a way to reach past the node's own box, and each is named in\n * `repairs` so a route can tell the model what it lost.\n */\nconst STRIPPED_NODE_KEYS = [\n 'className',\n 'interactions',\n 'styleOverrides',\n 'attrOverrides',\n 'presetRef',\n] as const\n\nconst SCREEN_ID = /^[A-Za-z0-9_-]{1,64}$/\n/** The field an icon is picked with; its value carries a drawing no model can write. */\nconst ICON_PICKER_FIELD = 'icon-picker'\nconst MAX_SX_KEYS = 40\n/** A string prop with no role of its own — a color, a CSS length, an icon id. */\nconst PLAIN_STRING_MAX = 500\n\nconst paletteTokens = new Set<string>(AI_SX_TOKENS.palette)\nconst breakpoints = new Set<string>(AI_SX_TOKENS.breakpoints)\nconst typographyVariants = new Set<string>(AI_SX_TOKENS.typographyVariants)\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** An `https:` address that parses as one. */\nexport function isHttpsUrl(value: string): boolean {\n if (!/^https:\\/\\//i.test(value)) return false\n try {\n return new URL(value).protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/** A path on this site: one leading slash, never a scheme-relative `//`. */\nexport function isRootRelativePath(value: string): boolean {\n return /^\\/(?!\\/)[^\\s<>\"'`\\\\]*$/.test(value)\n}\n\nfunction sxValueAllowed(key: string, value: unknown): boolean {\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'string') return false\n const trimmed = value.trim()\n if (!trimmed || HOSTILE_TEXT.test(trimmed)) return false\n if (paletteTokens.has(trimmed)) return true\n if (key === 'typography' && typographyVariants.has(trimmed)) return true\n return SX_VALUE.test(trimmed)\n}\n\n/**\n * The allowed slice of an `sx` object, with a line per key or value dropped.\n * Responsive objects keep only breakpoint keys; anything deeper — a\n * pseudo-selector, a nested selector, an array — is not a value at all.\n */\nfunction sanitizeSx(\n nodeId: string,\n sx: unknown,\n repairs: string[],\n): Record<string, unknown> | undefined {\n if (!isRecord(sx)) {\n if (sx !== undefined)\n repairs.push(`${nodeId}: sx is not an object; dropped`)\n return undefined\n }\n const out: Record<string, unknown> = {}\n let kept = 0\n for (const [key, value] of Object.entries(sx)) {\n if (!AI_SX_ALLOWED_KEYS.has(key)) {\n repairs.push(`${nodeId}: sx.${key} is not an allowed style; dropped`)\n continue\n }\n if (kept >= MAX_SX_KEYS) {\n repairs.push(\n `${nodeId}: sx.${key} is past the ${MAX_SX_KEYS}-key ceiling; dropped`,\n )\n continue\n }\n if (isRecord(value)) {\n const responsive: Record<string, unknown> = {}\n for (const [breakpoint, inner] of Object.entries(value)) {\n if (!breakpoints.has(breakpoint)) {\n repairs.push(\n `${nodeId}: sx.${key}.${breakpoint} is not a breakpoint; dropped`,\n )\n continue\n }\n if (!sxValueAllowed(key, inner)) {\n repairs.push(\n `${nodeId}: sx.${key}.${breakpoint} is not an allowed value; dropped`,\n )\n continue\n }\n responsive[breakpoint] =\n typeof inner === 'string' ? inner.trim() : inner\n }\n if (Object.keys(responsive).length) {\n out[key] = responsive\n kept += 1\n }\n continue\n }\n if (!sxValueAllowed(key, value)) {\n repairs.push(`${nodeId}: sx.${key} is not an allowed value; dropped`)\n continue\n }\n out[key] = typeof value === 'string' ? value.trim() : value\n kept += 1\n }\n return kept ? out : undefined\n}\n\nfunction coerceEnum(schema: AiPropSchema, value: unknown): string | undefined {\n if (typeof value !== 'string' || !schema.enum) return undefined\n const wanted = value.trim().toLowerCase()\n return schema.enum.find((option) => option.toLowerCase() === wanted)\n}\n\nfunction coerceBoolean(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value\n if (value === 'true') return true\n if (value === 'false') return false\n return undefined\n}\n\nfunction coerceNumber(\n schema: AiPropSchema,\n value: unknown,\n): number | undefined {\n const parsed =\n typeof value === 'number'\n ? value\n : typeof value === 'string' && value.trim() !== ''\n ? Number(value)\n : NaN\n if (!Number.isFinite(parsed)) return undefined\n if (schema.type === 'integer' && !Number.isInteger(parsed)) return undefined\n if (schema.minimum !== undefined && parsed < schema.minimum) return undefined\n if (schema.maximum !== undefined && parsed > schema.maximum) return undefined\n return parsed\n}\n\nfunction textLimitFor(\n entry: AiPaletteEntry,\n name: string,\n props: Record<string, unknown>,\n): number {\n const declared = entry.textLimits[name]\n // A heading's copy is held to the headline ceiling, decided by the variant\n // the same node carries; anything else keeps the ceiling the palette\n // assigned to the prop.\n if (name === 'children' && isHeadlineVariant(props.variant)) {\n return Math.min(declared ?? AI_TEXT_LIMITS.body, AI_TEXT_LIMITS.headline)\n }\n return declared ?? AI_TEXT_LIMITS.body\n}\n\n/**\n * One string prop against its role. Returns the value to store, or\n * `undefined` with the reason pushed to `repairs`. A binding token the\n * caller admitted passes a `url` or `media` role whole.\n */\nfunction sanitizeString(\n nodeId: string,\n entry: AiPaletteEntry,\n name: string,\n schema: AiPropSchema,\n raw: string,\n props: Record<string, unknown>,\n screenIds: Set<string> | null,\n assetIds: Set<string> | null,\n repairs: string[],\n bindingTokens: ReadonlySet<string> | null = null,\n): string | undefined {\n let value = raw.trim()\n if (HOSTILE_TEXT.test(value)) {\n repairs.push(`${nodeId}.${name} carried markup or script; dropped`)\n return undefined\n }\n // An icon is drawn from the path its picker stores beside the id, which a\n // model can never supply, so an icon a model names draws nothing where the\n // page is published (AGL-3054). The site owner picks it.\n if (entry.propFields[name] === ICON_PICKER_FIELD) {\n repairs.push(`${nodeId}.${name} is an icon, which the site owner picks from the library; dropped`)\n return undefined\n }\n const role = entry.propRoles[name]\n switch (role) {\n case 'text': {\n const limit = textLimitFor(entry, name, props)\n if (value.length > limit) {\n value = value.slice(0, limit).trimEnd()\n repairs.push(\n `${nodeId}.${name} was over ${limit} characters; truncated`,\n )\n }\n break\n }\n case 'url': {\n if (bindingTokens?.has(value)) break\n if (!isHttpsUrl(value) && !isRootRelativePath(value)) {\n repairs.push(\n `${nodeId}.${name} is neither an https: URL nor a path on this site; dropped`,\n )\n return undefined\n }\n break\n }\n case 'screen': {\n if (screenIds ? !screenIds.has(value) : !SCREEN_ID.test(value)) {\n repairs.push(\n `${nodeId}.${name} names a page this site does not have; dropped`,\n )\n return undefined\n }\n break\n }\n case 'media': {\n if (bindingTokens?.has(value)) break\n if (isHttpsUrl(value)) break\n const ref = parseMediaRef(value)\n if (!ref || (assetIds && !assetIds.has(ref.mediaId))) {\n repairs.push(\n `${nodeId}.${name} is neither a media reference this site holds nor an https: URL; dropped`,\n )\n return undefined\n }\n break\n }\n default: {\n if (value.length > PLAIN_STRING_MAX) {\n repairs.push(\n `${nodeId}.${name} is over ${PLAIN_STRING_MAX} characters; dropped`,\n )\n return undefined\n }\n }\n }\n if (schema.pattern && !new RegExp(schema.pattern).test(value)) {\n repairs.push(`${nodeId}.${name} does not match its pattern; dropped`)\n return undefined\n }\n if (schema.minLength !== undefined && value.length < schema.minLength) {\n repairs.push(\n `${nodeId}.${name} is shorter than ${schema.minLength} characters; dropped`,\n )\n return undefined\n }\n if (schema.maxLength !== undefined && value.length > schema.maxLength) {\n value = value.slice(0, schema.maxLength).trimEnd()\n repairs.push(\n `${nodeId}.${name} was over ${schema.maxLength} characters; truncated`,\n )\n }\n return value\n}\n\n/**\n * The element's own declarations plus the node capabilities every element\n * carries (AGL-3156) — repeating, today. Two shapes only, so they are built\n * once each rather than per node: a capability prop is the same on every\n * element with the same child contract.\n *\n * The element wins a name collision. A component that declared a prop of its\n * own under a capability's name would be describing its own attribute, and the\n * validator has to hold it to that.\n */\nconst CAPABLE_ENTRIES = new WeakMap<AiPaletteEntry, AiPaletteEntry>()\n\nfunction withNodeCapabilities(entry: AiPaletteEntry): AiPaletteEntry {\n const cached = CAPABLE_ENTRIES.get(entry)\n if (cached) return cached\n const capability = nodeCapabilityProps(\n AI_NODE_CAPABILITIES,\n entry.acceptsChildren,\n )\n const merged: AiPaletteEntry = {\n ...entry,\n propsSchema: {\n ...entry.propsSchema,\n properties: {\n ...capability.properties,\n ...entry.propsSchema.properties,\n },\n },\n propRoles: { ...capability.propRoles, ...entry.propRoles },\n textLimits: { ...capability.textLimits, ...entry.textLimits },\n }\n CAPABLE_ENTRIES.set(entry, merged)\n return merged\n}\n\nfunction sanitizeProps(\n nodeId: string,\n paletteEntry: AiPaletteEntry,\n raw: unknown,\n screenIds: Set<string> | null,\n assetIds: Set<string> | null,\n repairs: string[],\n bindingTokens: ReadonlySet<string> | null = null,\n): { props: Record<string, unknown> } | { missing: string } {\n const entry = withNodeCapabilities(paletteEntry)\n const source = isRecord(raw) ? raw : {}\n const props: Record<string, unknown> = {}\n const declared = entry.propsSchema.properties\n for (const [name, value] of Object.entries(source)) {\n // `sx` is read off the node instead; a model that put it here meant the\n // same thing, and the caller has already moved it.\n if (name === 'sx' || value === undefined || value === null) continue\n const schema = declared[name]\n if (!schema) {\n repairs.push(\n `${nodeId}.${name} is not a prop of ${entry.displayName}; dropped`,\n )\n continue\n }\n if (schema.enum) {\n const coerced = coerceEnum(schema, value)\n if (coerced === undefined) {\n repairs.push(\n `${nodeId}.${name} is not one of ${schema.enum.join('|')}; dropped`,\n )\n continue\n }\n if (coerced !== value) {\n repairs.push(\n `${nodeId}.${name} was spelled \"${String(value)}\"; normalized to \"${coerced}\"`,\n )\n }\n props[name] = coerced\n continue\n }\n switch (schema.type) {\n case 'boolean': {\n const coerced = coerceBoolean(value)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.${name} is not a boolean; dropped`)\n continue\n }\n props[name] = coerced\n break\n }\n case 'number':\n case 'integer': {\n const coerced = coerceNumber(schema, value)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.${name} is not a number in range; dropped`)\n continue\n }\n props[name] = coerced\n break\n }\n default: {\n const text =\n typeof value === 'string'\n ? value\n : typeof value === 'number' || typeof value === 'boolean'\n ? String(value)\n : undefined\n if (text === undefined) {\n repairs.push(`${nodeId}.${name} is not text; dropped`)\n continue\n }\n const cleaned = sanitizeString(\n nodeId,\n entry,\n name,\n schema,\n text,\n source,\n screenIds,\n assetIds,\n repairs,\n bindingTokens,\n )\n if (cleaned === undefined) continue\n props[name] = cleaned\n }\n }\n }\n for (const name of entry.propsSchema.required) {\n if (props[name] === undefined) return { missing: name }\n }\n return { props }\n}\n\n/** Surfaces that compose page elements, and so may place a component instance. */\nconst INSTANCE_SURFACES: ReadonlySet<AiSurface> = new Set([\n 'screen',\n 'layout',\n 'component',\n])\n\nconst FORM_COMPONENT_ID = 'form'\n\n/** The instance prop naming the component it places; stored on every instance node. */\nexport const AI_INSTANCE_REF_PROP = 'refId'\n\ninterface ReferenceSets {\n componentIds: Set<string> | null\n componentProps: Record<string, AiComponentPropTypes>\n formIds: Set<string> | null\n datasetIds: Set<string> | null\n assetIds: Set<string> | null\n /** The tree defines a reusable component, which may hand its own property tokens on. */\n definesComponent: boolean\n}\n\n/**\n * One value an instance fills in for a declared prop, held to what the\n * declaration's type can hold: a picture from the library, a link, a number,\n * a switch, or copy.\n */\nfunction instancePropValue(\n nodeId: string,\n name: string,\n type: string,\n raw: unknown,\n assetIds: Set<string> | null,\n repairs: string[],\n): unknown {\n if (type === 'number') {\n const parsed = typeof raw === 'number' ? raw : Number(raw)\n if (raw === '' || !Number.isFinite(parsed)) {\n repairs.push(`${nodeId}.propValues.${name} is not a number; dropped`)\n return undefined\n }\n return parsed\n }\n if (type === 'boolean') {\n const coerced = coerceBoolean(raw)\n if (coerced === undefined) {\n repairs.push(`${nodeId}.propValues.${name} is not a boolean; dropped`)\n }\n return coerced\n }\n if (type === 'icon') {\n // A page cannot fill an icon: the pick is an id and the path drawn from\n // it, and a word in its place draws nothing (AGL-3054). The property is\n // left for the site owner to pick.\n repairs.push(`${nodeId}.propValues.${name} is an icon, which the site owner picks from the library; dropped`)\n return undefined\n }\n if (typeof raw !== 'string' && typeof raw !== 'number' && typeof raw !== 'boolean') {\n repairs.push(`${nodeId}.propValues.${name} is not a value; dropped`)\n return undefined\n }\n const value = String(raw).trim()\n if (HOSTILE_TEXT.test(value)) {\n repairs.push(`${nodeId}.propValues.${name} carried markup or script; dropped`)\n return undefined\n }\n if (type === 'image') {\n if (isHttpsUrl(value)) return value\n const ref = parseMediaRef(value)\n if (!ref || (assetIds && !assetIds.has(ref.mediaId))) {\n repairs.push(\n `${nodeId}.propValues.${name} is neither a media reference this site holds nor an https: URL; dropped`,\n )\n return undefined\n }\n return value\n }\n if (type === 'href') {\n if (isHttpsUrl(value) || isRootRelativePath(value)) return value\n repairs.push(\n `${nodeId}.propValues.${name} is neither an https: URL nor a path on this site; dropped`,\n )\n return undefined\n }\n if (value.length > AI_TEXT_LIMITS.body) {\n repairs.push(\n `${nodeId}.propValues.${name} was over ${AI_TEXT_LIMITS.body} characters; truncated`,\n )\n return value.slice(0, AI_TEXT_LIMITS.body).trimEnd()\n }\n return value\n}\n\n/**\n * The site-record props of one node — an instance's `refId` and\n * `propValues`, a Form's `formId` and `datasetId` — checked against the ids\n * the caller supplied. They leave `props` before the palette pass, which\n * would drop them as undeclared, and come back in `kept` once checked.\n */\nfunction sanitizeReferenceProps(\n nodeId: string,\n componentId: string,\n props: Record<string, unknown>,\n refs: ReferenceSets,\n repairs: string[],\n): { props: Record<string, unknown>; kept: Record<string, unknown> } | { refusal: string } {\n const rest: Record<string, unknown> = { ...props }\n const kept: Record<string, unknown> = {}\n if (componentId === REUSABLE_INSTANCE_COMPONENT_ID) {\n const refId = typeof rest[AI_INSTANCE_REF_PROP] === 'string' ? String(rest[AI_INSTANCE_REF_PROP]).trim() : ''\n delete rest[AI_INSTANCE_REF_PROP]\n if (!refId || !refs.componentIds?.has(refId)) {\n return {\n refusal: `Reusable Component (${nodeId}) names a component this site does not have`,\n }\n }\n kept[AI_INSTANCE_REF_PROP] = refId\n const rawValues = rest[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n delete rest[REUSABLE_INSTANCE_PROP_VALUES_KEY]\n if (rawValues !== undefined && !isRecord(rawValues)) {\n repairs.push(`${nodeId}.propValues is not an object; dropped`)\n }\n const declared = refs.componentProps[refId]\n const values: Record<string, unknown> = {}\n for (const [name, raw] of Object.entries(isRecord(rawValues) ? rawValues : {})) {\n if (raw === undefined || raw === null) continue\n if (!COMPONENT_PROP_NAME_PATTERN.test(name)) {\n repairs.push(`${nodeId}.propValues.${name} is not a prop name; dropped`)\n continue\n }\n if (declared && !(name in declared)) {\n repairs.push(`${nodeId}.propValues.${name} is not a prop of that component; dropped`)\n continue\n }\n if (refs.definesComponent && isAiComponentPropToken(raw)) {\n // A property of the component being defined, handed on whole; the\n // component step's check holds it to the kind this prop declares.\n values[name] = raw.trim()\n continue\n }\n const value = instancePropValue(\n nodeId,\n name,\n declared?.[name] ?? 'text',\n raw,\n refs.assetIds,\n repairs,\n )\n if (value !== undefined) values[name] = value\n }\n if (Object.keys(values).length) kept[REUSABLE_INSTANCE_PROP_VALUES_KEY] = values\n return { props: rest, kept }\n }\n if (componentId === FORM_COMPONENT_ID) {\n const bindings: Array<[prop: string, ids: Set<string> | null, record: string]> = [\n ['formId', refs.formIds, 'form'],\n ['datasetId', refs.datasetIds, 'dataset'],\n ]\n for (const [prop, ids, record] of bindings) {\n const raw = rest[prop]\n delete rest[prop]\n if (raw === undefined || raw === null || raw === '') continue\n const id = typeof raw === 'string' ? raw.trim() : ''\n if (id && ids?.has(id)) {\n kept[prop] = id\n } else {\n repairs.push(`${nodeId}.${prop} names a ${record} this site does not have; dropped`)\n }\n }\n }\n return { props: rest, kept }\n}\n\nfunction refusalCode(error: string): AiNodeTreeRefusalCode {\n if (/too large/i.test(error)) return 'too-large'\n if (/cannot be published/i.test(error)) return 'component'\n return 'structure'\n}\n\n/**\n * Whether the root the model wrote is the surface's root: the canvas wrapper\n * for a document surface (which the sanitizer exempts by the same shape), or\n * the named component otherwise.\n */\nfunction rootMatches(surfaceRoot: string, componentId: unknown): boolean {\n if (surfaceRoot === 'div') {\n return componentId == null || componentId === '' || componentId === 'div'\n }\n return componentId === surfaceRoot\n}\n\nexport function validateAiNodeTree(\n input: unknown,\n surface: AiSurface,\n context?: AiNodeTreeContext,\n): AiNodeTreeResult {\n try {\n return validate(input, surface, context)\n } catch (error) {\n return {\n ok: false,\n code: 'invalid-input',\n error: `The tree could not be read: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n}\n\nexport type AiNodePatchResult =\n | {\n ok: true\n props: Record<string, unknown>\n sx: Record<string, unknown> | undefined\n repairs: string[]\n }\n | { ok: false; error: string; code: AiNodeTreeRefusalCode }\n\n/**\n * One element's prop and style patch, held to the rules `validateAiNodeTree`\n * holds an element it places — for an edit to an element already on the\n * canvas, where there is no tree to validate.\n *\n * The same passes run: unknown props dropped, enums coerced, every string\n * held to its role and screened for markup, `sx` held to the key set and the\n * value grammar. The one difference is `required`: a patch is merged over the\n * props the element already has, so a required prop the patch does not name\n * is not missing.\n */\nexport function validateAiNodePatch(\n componentId: string,\n patch: { props?: unknown; sx?: unknown },\n context?: AiNodeTreeContext,\n): AiNodePatchResult {\n const entry = AI_PALETTE[componentId]\n if (!entry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${componentId}\" is not in the palette`,\n }\n }\n const repairs: string[] = []\n const props = sanitizeProps(\n componentId,\n { ...entry, propsSchema: { ...entry.propsSchema, required: [] } },\n patch.props,\n context?.screenIds ? new Set(context.screenIds) : null,\n context?.assetIds ? new Set(context.assetIds) : null,\n repairs,\n )\n const sx =\n patch.sx === undefined ? undefined : sanitizeSx(componentId, patch.sx, repairs)\n return {\n ok: true,\n props: 'props' in props ? props.props : {},\n sx,\n repairs,\n }\n}\n\nfunction validate(\n input: unknown,\n surface: AiSurface,\n context: AiNodeTreeContext | undefined,\n): AiNodeTreeResult {\n const definition = AI_SURFACES[surface]\n if (!definition) {\n return {\n ok: false,\n code: 'invalid-input',\n error: `Unknown surface \"${surface}\"`,\n }\n }\n if (\n !isRecord(input) ||\n typeof input.rootId !== 'string' ||\n !isRecord(input.nodes)\n ) {\n return {\n ok: false,\n code: 'invalid-input',\n error: 'Expected an object with a string rootId and a nodes map',\n }\n }\n const rootId = input.rootId\n const nodes = input.nodes as Record<string, unknown>\n const root = nodes[rootId]\n if (!isRecord(root)) {\n return {\n ok: false,\n code: 'structure',\n error: 'Definition has no root node',\n }\n }\n if (!rootMatches(definition.root, root.componentId)) {\n return {\n ok: false,\n code: 'root',\n error: `A ${surface} starts with ${definition.root === 'div' ? 'the document wrapper' : `\"${definition.root}\"`}, not \"${String(root.componentId)}\"`,\n }\n }\n\n // The sanitizer keeps only the persisted node keys, and `sx` is not one of\n // them for a listing. Read it off every node first — from the node field\n // or from `props.sx`, which a model writes just as readily — and put it\n // back, checked, on the nodes that survive.\n const sxByNodeId = new Map<string, unknown>()\n const hiddenByNodeId = new Set<string>()\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecord(node)) continue\n const sx =\n node.sx !== undefined\n ? node.sx\n : isRecord(node.props)\n ? node.props.sx\n : undefined\n if (sx !== undefined) sxByNodeId.set(id, sx)\n if (node.hidden === true) hiddenByNodeId.add(id)\n }\n\n // A DAM reference (a `media:` value) is not a URL the sanitizer's\n // `src` policy admits — a listing must not carry another tenant's asset —\n // but on the tenant's own page it is exactly what an image should name.\n // Lift those values out before sanitizing and hand them back to the prop\n // pass, which holds them to the asset set the caller supplied.\n // A binding token the caller admitted (AGL-2909) is lifted the same way:\n // `{{entry.coverImage}}` is no address the sanitizer knows, and the prop\n // pass holds it to the tokens the caller named.\n const bindingTokens = context?.bindingTokens ? new Set(context.bindingTokens) : null\n // A reusable component's own property tokens (AGL-2908) are lifted too, and\n // kept as written rather than handed back to the prop pass: `{{prop.variant}}`\n // is no value a dropdown, a switch, an address or a visibility directive\n // admits. Copy keeps its tokens without lifting, as the text they are.\n const definesComponent = context?.definesComponent === true\n const mediaByNodeId = new Map<string, Record<string, string>>()\n const boundByNodeId = new Map<string, Record<string, string>>()\n const forSanitizer: Record<string, unknown> = {}\n for (const [id, node] of Object.entries(nodes)) {\n if (!isRecord(node) || !isRecord(node.props)) {\n forSanitizer[id] = node\n continue\n }\n const entry = AI_PALETTE[String(node.componentId)]\n const roles = entry?.propRoles ?? {}\n const props: Record<string, unknown> = { ...node.props }\n const lifted: Record<string, string> = {}\n const bound: Record<string, string> = {}\n for (const [key, value] of Object.entries(props)) {\n if (typeof value !== 'string') continue\n const role = roles[key]\n if (\n definesComponent &&\n isAiComponentPropToken(value) &&\n (AI_COMPONENT_VISIBILITY_PROPS.includes(key) ||\n (entry?.propsSchema.properties[key] !== undefined && role !== 'text'))\n ) {\n bound[key] = value.trim()\n delete props[key]\n continue\n }\n if (\n (role === 'media' && parseMediaRef(value.trim())) ||\n ((role === 'media' || role === 'url') && bindingTokens?.has(value.trim()))\n ) {\n lifted[key] = value\n delete props[key]\n }\n }\n if (Object.keys(lifted).length) mediaByNodeId.set(id, lifted)\n if (Object.keys(bound).length) boundByNodeId.set(id, bound)\n forSanitizer[id] = { ...node, props }\n }\n\n const refs: ReferenceSets = {\n componentIds: context?.componentIds ? new Set(context.componentIds) : null,\n componentProps: context?.componentProps ?? {},\n formIds: context?.formIds ? new Set(context.formIds) : null,\n datasetIds: context?.datasetIds ? new Set(context.datasetIds) : null,\n assetIds: context?.assetIds ? new Set(context.assetIds) : null,\n definesComponent,\n }\n // An instance is admitted only where the caller grounded it in the site's\n // components; the reference itself is checked on the node below.\n const allow =\n refs.componentIds && INSTANCE_SURFACES.has(surface)\n ? [...definition.allow, REUSABLE_INSTANCE_COMPONENT_ID]\n : definition.allow\n const sanitized = sanitizePortableDefinition(\n { rootId, nodes: forSanitizer },\n { componentIds: allow },\n )\n if (sanitized.ok === false) {\n return {\n ok: false,\n code: refusalCode(sanitized.error),\n error: sanitized.error,\n }\n }\n\n const screenIds = context?.screenIds ? new Set(context.screenIds) : null\n const assetIds = refs.assetIds\n const repairs: string[] = []\n const output: NodesMap = {}\n const rootIsWrapper =\n rootMatches('div', root.componentId) && definition.root === 'div'\n const minted = new Map<string, string>()\n const mint = (id: string): string => {\n const fresh =\n id === rootId && rootIsWrapper\n ? CANVAS_ROOT_ELEMENT_ID\n : createIdUrlSafe()\n minted.set(id, fresh)\n return fresh\n }\n\n /** Each visited node's parent by the model's own ids, for the parentId check. */\n const parentOf = new Map<string, string>()\n const queue: Array<{ id: string; parentId: string | null }> = [\n { id: rootId, parentId: null },\n ]\n mint(rootId)\n while (queue.length) {\n const { id, parentId } = queue.shift() as {\n id: string\n parentId: string | null\n }\n const node = sanitized.nodes[id]\n const entry = AI_PALETTE[node.componentId]\n if (!entry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${node.componentId}\" is not in the palette`,\n }\n }\n const newId = minted.get(id) as string\n const raw = nodes[id]\n if (isRecord(raw)) {\n // The sanitizer drops silently; the model is told what went.\n for (const key of STRIPPED_NODE_KEYS) {\n if (raw[key] !== undefined) {\n repairs.push(\n `${id}.${key} is not something a generated node may set; dropped`,\n )\n }\n }\n if (raw.hidden !== undefined && typeof raw.hidden !== 'boolean') {\n repairs.push(`${id}.hidden is not a boolean; dropped`)\n }\n if (raw.nodes !== undefined && !Array.isArray(raw.nodes)) {\n repairs.push(\n `${id}.nodes is not a list of child ids; its children were dropped`,\n )\n }\n if (\n raw.parentId !== undefined &&\n raw.parentId !== null &&\n raw.parentId !== (parentId === null ? null : parentOf.get(id))\n ) {\n repairs.push(\n `${id}.parentId disagreed with where the tree places it; recomputed`,\n )\n }\n if (isRecord(raw.props)) {\n const lifted = mediaByNodeId.get(id) ?? {}\n const kept = boundByNodeId.get(id) ?? {}\n for (const key of Object.keys(raw.props)) {\n if (\n key !== 'sx' &&\n lifted[key] === undefined &&\n kept[key] === undefined &&\n node.props?.[key] === undefined &&\n raw.props[key] !== undefined\n ) {\n repairs.push(\n `${id}.${key} was an unsafe handler, address or markup; dropped`,\n )\n }\n }\n }\n }\n const children: string[] = []\n for (const childId of node.nodes ?? []) {\n if (minted.has(childId)) {\n repairs.push(\n `${childId} was listed under more than one parent; the later listing was dropped`,\n )\n continue\n }\n const child = sanitized.nodes[childId]\n if (!child) continue\n const childEntry = AI_PALETTE[child.componentId]\n if (!childEntry) {\n return {\n ok: false,\n code: 'component',\n error: `Component \"${child.componentId}\" is not in the palette`,\n }\n }\n if (!entry.acceptsChildren) {\n return {\n ok: false,\n code: 'lineage',\n error: `${entry.displayName} (${id}) cannot hold other elements, but lists ${childId}`,\n }\n }\n if (\n !linealRelationshipPermits(\n {\n componentId: child.componentId,\n pluginId: childEntry.pluginId,\n restrictParent: childEntry.restrictParent,\n },\n {\n componentId: node.componentId,\n pluginId: entry.pluginId,\n restrictChildren: entry.restrictChildren,\n },\n )\n ) {\n return {\n ok: false,\n code: 'lineage',\n error: `${childEntry.displayName} (${childId}) cannot be placed inside ${entry.displayName} (${id})`,\n }\n }\n children.push(mint(childId))\n parentOf.set(childId, id)\n queue.push({ id: childId, parentId: newId })\n }\n\n const reference = sanitizeReferenceProps(\n id,\n node.componentId,\n { ...node.props, ...mediaByNodeId.get(id) },\n refs,\n repairs,\n )\n if ('refusal' in reference) {\n return { ok: false, code: 'reference', error: reference.refusal }\n }\n // A required prop the component binds is present: its value is the token.\n const boundProps = boundByNodeId.get(id)\n const propsResult = sanitizeProps(\n id,\n boundProps\n ? {\n ...entry,\n propsSchema: {\n ...entry.propsSchema,\n required: entry.propsSchema.required.filter((name) => boundProps[name] === undefined),\n },\n }\n : entry,\n reference.props,\n screenIds,\n assetIds,\n repairs,\n bindingTokens,\n )\n if ('missing' in propsResult) {\n return {\n ok: false,\n code: 'required-prop',\n error: `${entry.displayName} (${id}) is missing its required \"${propsResult.missing}\" prop`,\n }\n }\n const sx = sanitizeSx(id, sxByNodeId.get(id), repairs)\n output[newId] = {\n $id: newId,\n type: NodeType.NODE,\n componentId: node.componentId,\n pluginId: entry.pluginId,\n parentId,\n nodes: children,\n props: { ...propsResult.props, ...reference.kept, ...boundProps },\n ...(sx ? { sx } : {}),\n ...(hiddenByNodeId.has(id) ? { hidden: true } : {}),\n }\n }\n\n // The save's own measurement (msgpack), not a JSON string length: the\n // ceiling is stated in those bytes, and a UTF-16 length reads multi-byte\n // copy as smaller than the document the save will refuse.\n const bytes = nodeMapBytes(output)\n if (bytes > NODE_MAP_MAX_BYTES) {\n return {\n ok: false,\n code: 'too-large',\n error: `The tree is ${bytes} bytes; the stored ceiling is ${NODE_MAP_MAX_BYTES}`,\n }\n }\n const sourceIds: Record<string, string> = {}\n for (const [source, fresh] of minted) sourceIds[fresh] = source\n return {\n ok: true,\n rootId: minted.get(rootId) as string,\n nodes: output,\n repairs,\n sourceIds,\n }\n}\n"],"names":["AI_TEXT_LIMITS","isHeadlineVariant","nodeCapabilityProps","AI_NODE_CAPABILITIES","AI_PALETTE","AI_SURFACES","AI_SX_TOKENS","linealRelationshipPermits","NODE_MAP_MAX_BYTES","nodeMapBytes","parseMediaRef","COMPONENT_PROP_NAME_PATTERN","REUSABLE_INSTANCE_COMPONENT_ID","REUSABLE_INSTANCE_PROP_VALUES_KEY","createIdUrlSafe","CANVAS_ROOT_ELEMENT_ID","NodeType","AI_COMPONENT_VISIBILITY_PROPS","isAiComponentPropToken","sanitizePortableDefinition","AI_SX_ALLOWED_KEYS","Set","HOSTILE_TEXT","SX_VALUE","STRIPPED_NODE_KEYS","SCREEN_ID","ICON_PICKER_FIELD","MAX_SX_KEYS","PLAIN_STRING_MAX","paletteTokens","palette","breakpoints","typographyVariants","isRecord","value","Array","isArray","isHttpsUrl","test","URL","protocol","isRootRelativePath","sxValueAllowed","key","Number","isFinite","trimmed","trim","has","sanitizeSx","nodeId","sx","repairs","undefined","push","out","kept","Object","entries","responsive","breakpoint","inner","keys","length","coerceEnum","schema","enum","wanted","toLowerCase","find","option","coerceBoolean","coerceNumber","parsed","NaN","type","isInteger","minimum","maximum","textLimitFor","entry","name","props","declared","textLimits","variant","Math","min","body","headline","sanitizeString","raw","screenIds","assetIds","bindingTokens","propFields","role","propRoles","limit","slice","trimEnd","ref","mediaId","pattern","RegExp","minLength","maxLength","CAPABLE_ENTRIES","WeakMap","withNodeCapabilities","cached","get","capability","acceptsChildren","merged","propsSchema","properties","set","sanitizeProps","paletteEntry","source","displayName","coerced","join","String","text","cleaned","required","missing","INSTANCE_SURFACES","FORM_COMPONENT_ID","AI_INSTANCE_REF_PROP","instancePropValue","sanitizeReferenceProps","componentId","refs","rest","refId","componentIds","refusal","rawValues","componentProps","values","definesComponent","bindings","formIds","datasetIds","prop","ids","record","id","refusalCode","error","rootMatches","surfaceRoot","validateAiNodeTree","input","surface","context","validate","ok","code","Error","message","validateAiNodePatch","patch","definition","rootId","nodes","root","sxByNodeId","Map","hiddenByNodeId","node","hidden","add","mediaByNodeId","boundByNodeId","forSanitizer","roles","lifted","bound","includes","allow","sanitized","output","rootIsWrapper","minted","mint","fresh","parentOf","queue","parentId","shift","newId","children","childId","child","childEntry","pluginId","restrictParent","restrictChildren","reference","boundProps","propsResult","filter","$id","NODE","bytes","sourceIds"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,cAAc,EACdC,iBAAiB,EACjBC,mBAAmB,QAId,kBAAc;AACrB,SACEC,oBAAoB,EACpBC,UAAU,EACVC,WAAW,EACXC,YAAY,QACP,4BAAwB;AAC/B,SAASC,yBAAyB,QAAQ,sCAAqC;AAC/E,SACEC,kBAAkB,EAClBC,YAAY,QACP,0CAAyC;AAChD,SAASC,aAAa,QAAQ,mCAAkC;AAChE,SACEC,2BAA2B,EAC3BC,8BAA8B,EAC9BC,iCAAiC,QAC5B,iDAAgD;AACvD,SAASC,eAAe,QAAQ,wCAAuC;AACvE,SAASC,sBAAsB,QAAQ,2CAA0C;AACjF,SAASC,QAAQ,QAAuB,2BAA0B;AAElE,SACEC,6BAA6B,EAC7BC,sBAAsB,QACjB,6BAAyB;AAEhC,SAASC,0BAA0B,QAAQ,mDAAkD;AAiH7F;;;;;CAKC,GACD,OAAO,MAAMC,qBAA0C,IAAIC,IAAI;IAC7D,UAAU;IACV;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,QAAQ;IACR;IACA;IACA;IACA;IACA;IACA,aAAa;IACb;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,SAAS;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,UAAU;IACV;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,UAAU;IACV;IACA;IACA,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,EAAC;AAEF;;;;CAIC,GACD,OAAO,MAAMC,eACX,2LAA0L;AAE5L;;;;;;CAMC,GACD,OAAO,MAAMC,WAAW,qCAAoC;AAE5D;;;;C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"}
|
|
@@ -19,7 +19,7 @@ import type { JsonLinealOrder } from '@aglyn/aglyn/app-utils/lineal-order';
|
|
|
19
19
|
* The element palette as a model sees it (AGL-2905).
|
|
20
20
|
*
|
|
21
21
|
* `ai-palette.generated.ts` is produced from the registered plugin bundles by
|
|
22
|
-
* `
|
|
22
|
+
* `libs/plugins/ai/scripts/generate-ai-palette.mts` and holds the data; this module
|
|
23
23
|
* holds the shapes that data is typed against and the small readers over it,
|
|
24
24
|
* so a regeneration never rewrites a type.
|
|
25
25
|
*/
|
|
@@ -228,6 +228,12 @@ export declare const AI_EMAIL_CLIP_BYTES = 102000;
|
|
|
228
228
|
* page loads, so it carries the least imagery; a form is fields and a
|
|
229
229
|
* button; an email is held to the clipping threshold on its rendered HTML.
|
|
230
230
|
* No kind names a font beyond the theme's.
|
|
231
|
+
*
|
|
232
|
+
* A third-party player is a page's or a component's to carry, and only where
|
|
233
|
+
* a confirmed plan lists it (rule 16). A template is rendered for every
|
|
234
|
+
* record it serves and a layout around every page, so neither carries one
|
|
235
|
+
* (AGL-3433): an embed there loads a stranger's code on every page of a
|
|
236
|
+
* collection or a site.
|
|
231
237
|
*/
|
|
232
238
|
export declare const AI_OUTPUT_BUDGETS: Record<AiOutputKind, AiOutputBudget>;
|
|
233
239
|
/**
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
|
-
* GENERATED by
|
|
18
|
+
* GENERATED by libs/plugins/ai/scripts/generate-ai-palette.mts from the registered
|
|
19
19
|
* plugin bundles (AGL-2905). Do not edit; re-run the generator, and
|
|
20
20
|
* `npm run check:ai-palette` fails CI when this file is stale.
|
|
21
21
|
*/
|