@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
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { AI_USAGE_METER_ID } from "./constants.js";
|
|
17
|
+
import { ASSIST_CREDIT_COST_USD, ASSIST_PROVIDER_COST_FIELD } from "./usage/assist-credits.js";
|
|
18
|
+
/**
|
|
19
|
+
* THE AI PLUGIN'S METER (AGL-3080): provider spend, in the platform's cost
|
|
20
|
+
* model and against the credit band in its utilization table. Compiled into
|
|
21
|
+
* core by the manifest generator (`register.usageAxes`).
|
|
22
|
+
*
|
|
23
|
+
* The rollup records `assistCostUsd` — what the month's tokens cost US, at
|
|
24
|
+
* the provider's rates (AGL-3015) — so the axis enters the cost model at ×1
|
|
25
|
+
* and names no rate. It is the one line on the platform whose unit cost is
|
|
26
|
+
* real money paid to a third party, and the only one that can clear the
|
|
27
|
+
* $2/site floor on its own, which is why a margin surface that could not see
|
|
28
|
+
* it would rate a token-heavy workspace exactly as it rates an idle one.
|
|
29
|
+
*
|
|
30
|
+
* LIVE, because the rollup is a snapshot from when the cron ran and assist
|
|
31
|
+
* keeps spending after it: a reader that fetches the month's
|
|
32
|
+
* `assistUsage/{month}` document takes the provider figure, or the billed
|
|
33
|
+
* one on a month closed before the two were split, over the snapshot.
|
|
34
|
+
*
|
|
35
|
+
* The band is sold in credits, and a credit is `ASSIST_CREDIT_COST_USD` of
|
|
36
|
+
* spend, so what was used is the dollars over that — rounded up, the one
|
|
37
|
+
* conversion the customer's own meter uses.
|
|
38
|
+
*
|
|
39
|
+
* On the usage BUDGET the month's spend is the billed figure (`estCostUsd`),
|
|
40
|
+
* shown to the customer in credits and never in dollars: the dollars are our
|
|
41
|
+
* cost, and publishing them would put our model choice and our margin on a
|
|
42
|
+
* billing page. It counts toward the budget only from the month
|
|
43
|
+
* `BILL_ASSIST_TOKENS_FROM` names, because assist is a plan entitlement with
|
|
44
|
+
* no per-token price until then, and a budget that added it would be a
|
|
45
|
+
* surprise bill invented by a notification.
|
|
46
|
+
*/ /** The AI plugin's line of a workspace's monthly spend, by the id it declares. */ export const AI_ASSIST_SPEND_LINE_ID = 'assist';
|
|
47
|
+
export function aiUsageAxes() {
|
|
48
|
+
return {
|
|
49
|
+
costAxes: [
|
|
50
|
+
{
|
|
51
|
+
id: 'assist',
|
|
52
|
+
order: 80,
|
|
53
|
+
fields: [
|
|
54
|
+
'assistCostUsd'
|
|
55
|
+
],
|
|
56
|
+
// The month's credit draw and the overage that entered `billedCents`
|
|
57
|
+
// (AGL-2930), which the usage sweep writes beside the spend: the
|
|
58
|
+
// staff usage table's AI columns, `null` on a rollup written before
|
|
59
|
+
// the sweep recorded credits, and never a cost.
|
|
60
|
+
staffFields: [
|
|
61
|
+
'assistCredits',
|
|
62
|
+
'assistOverageUsd'
|
|
63
|
+
],
|
|
64
|
+
live: {
|
|
65
|
+
collection: 'assistUsage',
|
|
66
|
+
fields: [
|
|
67
|
+
ASSIST_PROVIDER_COST_FIELD,
|
|
68
|
+
'estCostUsd'
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
bands: [
|
|
74
|
+
{
|
|
75
|
+
id: 'assistCredits',
|
|
76
|
+
label: 'Assist credits',
|
|
77
|
+
order: 90,
|
|
78
|
+
fields: [
|
|
79
|
+
'assistCostUsd'
|
|
80
|
+
],
|
|
81
|
+
entitlement: 'assistCreditsPerMonth',
|
|
82
|
+
unitCostUsd: ASSIST_CREDIT_COST_USD
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
meters: [
|
|
86
|
+
{
|
|
87
|
+
id: AI_USAGE_METER_ID
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
spendLines: [
|
|
91
|
+
{
|
|
92
|
+
id: AI_ASSIST_SPEND_LINE_ID,
|
|
93
|
+
label: 'Assist',
|
|
94
|
+
live: {
|
|
95
|
+
collection: 'assistUsage',
|
|
96
|
+
field: 'estCostUsd'
|
|
97
|
+
},
|
|
98
|
+
billedFromEnv: 'BILL_ASSIST_TOKENS_FROM',
|
|
99
|
+
unit: {
|
|
100
|
+
costUsd: ASSIST_CREDIT_COST_USD,
|
|
101
|
+
label: 'Assist credits'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=usage-axes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/usage-axes.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 { PluginUsageAxesDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-usage-axes'\nimport { AI_USAGE_METER_ID } from './constants'\nimport {\n ASSIST_CREDIT_COST_USD,\n ASSIST_PROVIDER_COST_FIELD,\n} from './usage/assist-credits'\n\n/**\n * THE AI PLUGIN'S METER (AGL-3080): provider spend, in the platform's cost\n * model and against the credit band in its utilization table. Compiled into\n * core by the manifest generator (`register.usageAxes`).\n *\n * The rollup records `assistCostUsd` — what the month's tokens cost US, at\n * the provider's rates (AGL-3015) — so the axis enters the cost model at ×1\n * and names no rate. It is the one line on the platform whose unit cost is\n * real money paid to a third party, and the only one that can clear the\n * $2/site floor on its own, which is why a margin surface that could not see\n * it would rate a token-heavy workspace exactly as it rates an idle one.\n *\n * LIVE, because the rollup is a snapshot from when the cron ran and assist\n * keeps spending after it: a reader that fetches the month's\n * `assistUsage/{month}` document takes the provider figure, or the billed\n * one on a month closed before the two were split, over the snapshot.\n *\n * The band is sold in credits, and a credit is `ASSIST_CREDIT_COST_USD` of\n * spend, so what was used is the dollars over that — rounded up, the one\n * conversion the customer's own meter uses.\n *\n * On the usage BUDGET the month's spend is the billed figure (`estCostUsd`),\n * shown to the customer in credits and never in dollars: the dollars are our\n * cost, and publishing them would put our model choice and our margin on a\n * billing page. It counts toward the budget only from the month\n * `BILL_ASSIST_TOKENS_FROM` names, because assist is a plan entitlement with\n * no per-token price until then, and a budget that added it would be a\n * surprise bill invented by a notification.\n */\n/** The AI plugin's line of a workspace's monthly spend, by the id it declares. */\nexport const AI_ASSIST_SPEND_LINE_ID = 'assist'\n\nexport function aiUsageAxes(): PluginUsageAxesDeclaration {\n return {\n costAxes: [\n {\n id: 'assist',\n order: 80,\n fields: ['assistCostUsd'],\n // The month's credit draw and the overage that entered `billedCents`\n // (AGL-2930), which the usage sweep writes beside the spend: the\n // staff usage table's AI columns, `null` on a rollup written before\n // the sweep recorded credits, and never a cost.\n staffFields: ['assistCredits', 'assistOverageUsd'],\n live: {\n collection: 'assistUsage',\n fields: [ASSIST_PROVIDER_COST_FIELD, 'estCostUsd'],\n },\n },\n ],\n bands: [\n {\n id: 'assistCredits',\n label: 'Assist credits',\n order: 90,\n fields: ['assistCostUsd'],\n entitlement: 'assistCreditsPerMonth',\n unitCostUsd: ASSIST_CREDIT_COST_USD,\n },\n ],\n meters: [{ id: AI_USAGE_METER_ID }],\n spendLines: [\n {\n id: AI_ASSIST_SPEND_LINE_ID,\n label: 'Assist',\n live: { collection: 'assistUsage', field: 'estCostUsd' },\n billedFromEnv: 'BILL_ASSIST_TOKENS_FROM',\n unit: { costUsd: ASSIST_CREDIT_COST_USD, label: 'Assist credits' },\n },\n ],\n }\n}\n"],"names":["AI_USAGE_METER_ID","ASSIST_CREDIT_COST_USD","ASSIST_PROVIDER_COST_FIELD","AI_ASSIST_SPEND_LINE_ID","aiUsageAxes","costAxes","id","order","fields","staffFields","live","collection","bands","label","entitlement","unitCostUsd","meters","spendLines","field","billedFromEnv","unit","costUsd"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,iBAAiB,QAAQ,iBAAa;AAC/C,SACEC,sBAAsB,EACtBC,0BAA0B,QACrB,4BAAwB;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,gFAAgF,GAChF,OAAO,MAAMC,0BAA0B,SAAQ;AAE/C,OAAO,SAASC;IACd,OAAO;QACLC,UAAU;YACR;gBACEC,IAAI;gBACJC,OAAO;gBACPC,QAAQ;oBAAC;iBAAgB;gBACzB,qEAAqE;gBACrE,iEAAiE;gBACjE,oEAAoE;gBACpE,gDAAgD;gBAChDC,aAAa;oBAAC;oBAAiB;iBAAmB;gBAClDC,MAAM;oBACJC,YAAY;oBACZH,QAAQ;wBAACN;wBAA4B;qBAAa;gBACpD;YACF;SACD;QACDU,OAAO;YACL;gBACEN,IAAI;gBACJO,OAAO;gBACPN,OAAO;gBACPC,QAAQ;oBAAC;iBAAgB;gBACzBM,aAAa;gBACbC,aAAad;YACf;SACD;QACDe,QAAQ;YAAC;gBAAEV,IAAIN;YAAkB;SAAE;QACnCiB,YAAY;YACV;gBACEX,IAAIH;gBACJU,OAAO;gBACPH,MAAM;oBAAEC,YAAY;oBAAeO,OAAO;gBAAa;gBACvDC,eAAe;gBACfC,MAAM;oBAAEC,SAASpB;oBAAwBY,OAAO;gBAAiB;YACnE;SACD;IACH;AACF"}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2026 Aglyn LLC
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/
|
|
17
|
-
import { type PageMarkdownNodes } from '@aglyn/aglyn/app-utils/page-markdown';
|
|
18
|
-
/**
|
|
19
|
-
* What a page SAYS, read off its node tree (AGL-2910): the text a search
|
|
20
|
-
* listing is written from, the headings, and the images — each heading and
|
|
21
|
-
* image by node id, because a content fix addresses the element it changes.
|
|
22
|
-
*
|
|
23
|
-
* The text is the page's Markdown representation, the one an agent is
|
|
24
|
-
* served, so what the listing summarizes is what a reader of the page gets.
|
|
25
|
-
* The walk starts where that serializer starts — the content region — and
|
|
26
|
-
* skips what it skips, through the same chrome predicate: a heading in the
|
|
27
|
-
* site's navigation is not the page's heading.
|
|
28
|
-
*/
|
|
29
|
-
/** How much of a page's text a prompt carries. A listing summarizes; it does not need the page. */
|
|
30
|
-
export declare const AI_SEO_PAGE_TEXT_MAX_CHARS = 2000;
|
|
31
|
-
export interface AiSeoHeading {
|
|
32
|
-
/** The element, or `null` for a heading inside rich text a fix cannot address. */
|
|
33
|
-
nodeId: string | null;
|
|
34
|
-
level: number;
|
|
35
|
-
text: string;
|
|
36
|
-
/** Whether a content fix can change it: a text component with plain text. */
|
|
37
|
-
editable: boolean;
|
|
38
|
-
}
|
|
39
|
-
export interface AiSeoImage {
|
|
40
|
-
nodeId: string;
|
|
41
|
-
src: string;
|
|
42
|
-
alt: string;
|
|
43
|
-
/** The nearest heading or text before it, which is what the image illustrates. */
|
|
44
|
-
context: string;
|
|
45
|
-
}
|
|
46
|
-
export interface AiSeoPageFacts {
|
|
47
|
-
/** The page's Markdown body, capped. */
|
|
48
|
-
text: string;
|
|
49
|
-
wordCount: number;
|
|
50
|
-
headings: AiSeoHeading[];
|
|
51
|
-
h1s: AiSeoHeading[];
|
|
52
|
-
images: AiSeoImage[];
|
|
53
|
-
imagesMissingAlt: AiSeoImage[];
|
|
54
|
-
/** The node the content region starts at; a new heading goes first inside it. */
|
|
55
|
-
contentRootId: string | null;
|
|
56
|
-
}
|
|
57
|
-
/** Read the facts a listing and an audit need from one page's node map. */
|
|
58
|
-
export declare function aiSeoPageFacts(nodes: PageMarkdownNodes | null | undefined, options?: {
|
|
59
|
-
rootId?: string;
|
|
60
|
-
maxChars?: number;
|
|
61
|
-
}): AiSeoPageFacts;
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2026 Aglyn LLC
|
|
4
|
-
*
|
|
5
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
* you may not use this file except in compliance with the License.
|
|
7
|
-
* You may obtain a copy of the License at
|
|
8
|
-
*
|
|
9
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
*
|
|
11
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
* See the License for the specific language governing permissions and
|
|
15
|
-
* limitations under the License.
|
|
16
|
-
*/ import { buildPageMarkdown, headingLevelOf, isPageChromeNode, pageContentRootId } from "@aglyn/aglyn/app-utils/page-markdown";
|
|
17
|
-
/**
|
|
18
|
-
* What a page SAYS, read off its node tree (AGL-2910): the text a search
|
|
19
|
-
* listing is written from, the headings, and the images — each heading and
|
|
20
|
-
* image by node id, because a content fix addresses the element it changes.
|
|
21
|
-
*
|
|
22
|
-
* The text is the page's Markdown representation, the one an agent is
|
|
23
|
-
* served, so what the listing summarizes is what a reader of the page gets.
|
|
24
|
-
* The walk starts where that serializer starts — the content region — and
|
|
25
|
-
* skips what it skips, through the same chrome predicate: a heading in the
|
|
26
|
-
* site's navigation is not the page's heading.
|
|
27
|
-
*/ /** How much of a page's text a prompt carries. A listing summarizes; it does not need the page. */ export const AI_SEO_PAGE_TEXT_MAX_CHARS = 2000;
|
|
28
|
-
/** How much text around an image describes it. */ const IMAGE_CONTEXT_MAX_CHARS = 160;
|
|
29
|
-
/** The deepest a walk goes, and it stops at a cycle — a node map is data. */ const MAX_DEPTH = 64;
|
|
30
|
-
/** The text components whose heading level `headingLevelOf` reads. */ const TEXT_COMPONENTS = new Set([
|
|
31
|
-
'muiTypography',
|
|
32
|
-
'muiInlineText',
|
|
33
|
-
'muiListItemText'
|
|
34
|
-
]);
|
|
35
|
-
const collapse = (value)=>value.replace(/\s+/g, ' ').trim();
|
|
36
|
-
/** Author HTML to its text, for a heading stored as rich text. */ function htmlText(html) {
|
|
37
|
-
return collapse(html.replace(/<[^>]*>/g, ' ').replace(/ /g, ' ').replace(/&/g, '&'));
|
|
38
|
-
}
|
|
39
|
-
/** A text component's own text, and whether it is plain (a fix can rewrite it). */ function textOf(props) {
|
|
40
|
-
const html = props['html'];
|
|
41
|
-
if (typeof html === 'string' && html.trim()) return {
|
|
42
|
-
text: htmlText(html),
|
|
43
|
-
plain: false
|
|
44
|
-
};
|
|
45
|
-
const children = props['children'];
|
|
46
|
-
return typeof children === 'string' ? {
|
|
47
|
-
text: collapse(children),
|
|
48
|
-
plain: true
|
|
49
|
-
} : {
|
|
50
|
-
text: '',
|
|
51
|
-
plain: true
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
const childIdsOf = (node)=>Array.isArray(node == null ? void 0 : node.nodes) ? (node == null ? void 0 : node.nodes).filter((id)=>typeof id === 'string') : [];
|
|
55
|
-
/** Read the facts a listing and an audit need from one page's node map. */ export function aiSeoPageFacts(nodes, options = {}) {
|
|
56
|
-
var _options_maxChars;
|
|
57
|
-
const empty = {
|
|
58
|
-
text: '',
|
|
59
|
-
wordCount: 0,
|
|
60
|
-
headings: [],
|
|
61
|
-
h1s: [],
|
|
62
|
-
images: [],
|
|
63
|
-
imagesMissingAlt: [],
|
|
64
|
-
contentRootId: null
|
|
65
|
-
};
|
|
66
|
-
if (!nodes) return empty;
|
|
67
|
-
const contentRootId = pageContentRootId(nodes, options.rootId);
|
|
68
|
-
if (!contentRootId) return empty;
|
|
69
|
-
const markdown = buildPageMarkdown({
|
|
70
|
-
nodes,
|
|
71
|
-
rootId: options.rootId
|
|
72
|
-
});
|
|
73
|
-
const text = markdown.trim().slice(0, (_options_maxChars = options.maxChars) != null ? _options_maxChars : AI_SEO_PAGE_TEXT_MAX_CHARS);
|
|
74
|
-
const headings = [];
|
|
75
|
-
const images = [];
|
|
76
|
-
let lastText = '';
|
|
77
|
-
const seen = new Set();
|
|
78
|
-
const visit = (id, depth)=>{
|
|
79
|
-
var _node_props, _node_componentId, _props_html;
|
|
80
|
-
if (depth > MAX_DEPTH || seen.has(id)) return;
|
|
81
|
-
const node = nodes[id];
|
|
82
|
-
if (!node) return;
|
|
83
|
-
seen.add(id);
|
|
84
|
-
if (isPageChromeNode(node)) return;
|
|
85
|
-
const props = (_node_props = node.props) != null ? _node_props : {};
|
|
86
|
-
const componentId = String((_node_componentId = node.componentId) != null ? _node_componentId : '');
|
|
87
|
-
if (TEXT_COMPONENTS.has(componentId)) {
|
|
88
|
-
const { text: own, plain } = textOf(props);
|
|
89
|
-
const level = headingLevelOf(props);
|
|
90
|
-
if (own) {
|
|
91
|
-
if (level > 0) headings.push({
|
|
92
|
-
nodeId: id,
|
|
93
|
-
level,
|
|
94
|
-
text: own,
|
|
95
|
-
editable: plain
|
|
96
|
-
});
|
|
97
|
-
lastText = own;
|
|
98
|
-
}
|
|
99
|
-
} else if (componentId === 'markdown' && typeof props['content'] === 'string') {
|
|
100
|
-
for (const line of String(props['content']).split('\n')){
|
|
101
|
-
const match = /^(#{1,6})\s+(.+)$/.exec(line.trim());
|
|
102
|
-
if (match) {
|
|
103
|
-
headings.push({
|
|
104
|
-
nodeId: null,
|
|
105
|
-
level: match[1].length,
|
|
106
|
-
text: collapse(match[2]),
|
|
107
|
-
editable: false
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
lastText = collapse(String(props['content'])).slice(0, IMAGE_CONTEXT_MAX_CHARS);
|
|
112
|
-
} else if (componentId === 'custom-html' && typeof ((_props_html = props['html']) != null ? _props_html : props['children']) === 'string') {
|
|
113
|
-
var _props_html1;
|
|
114
|
-
const html = String((_props_html1 = props['html']) != null ? _props_html1 : props['children']);
|
|
115
|
-
for (const match of html.matchAll(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi)){
|
|
116
|
-
headings.push({
|
|
117
|
-
nodeId: null,
|
|
118
|
-
level: Number(match[1]),
|
|
119
|
-
text: htmlText(match[2]),
|
|
120
|
-
editable: false
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
} else if (componentId === 'image') {
|
|
124
|
-
var _props_src;
|
|
125
|
-
const src = String((_props_src = props['src']) != null ? _props_src : '').trim();
|
|
126
|
-
if (src) {
|
|
127
|
-
var _props_alt;
|
|
128
|
-
images.push({
|
|
129
|
-
nodeId: id,
|
|
130
|
-
src,
|
|
131
|
-
alt: collapse(String((_props_alt = props['alt']) != null ? _props_alt : '')),
|
|
132
|
-
context: lastText.slice(0, IMAGE_CONTEXT_MAX_CHARS)
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
for (const childId of childIdsOf(node))visit(childId, depth + 1);
|
|
137
|
-
};
|
|
138
|
-
visit(contentRootId, 0);
|
|
139
|
-
return {
|
|
140
|
-
text,
|
|
141
|
-
wordCount: text ? text.split(/\s+/).filter(Boolean).length : 0,
|
|
142
|
-
headings,
|
|
143
|
-
h1s: headings.filter((heading)=>heading.level === 1),
|
|
144
|
-
images,
|
|
145
|
-
imagesMissingAlt: images.filter((image)=>!image.alt),
|
|
146
|
-
contentRootId
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
//# sourceMappingURL=seo-page-facts.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/seo-page-facts.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n buildPageMarkdown,\n headingLevelOf,\n isPageChromeNode,\n pageContentRootId,\n type PageMarkdownNode,\n type PageMarkdownNodes,\n} from '@aglyn/aglyn/app-utils/page-markdown'\n\n/**\n * What a page SAYS, read off its node tree (AGL-2910): the text a search\n * listing is written from, the headings, and the images — each heading and\n * image by node id, because a content fix addresses the element it changes.\n *\n * The text is the page's Markdown representation, the one an agent is\n * served, so what the listing summarizes is what a reader of the page gets.\n * The walk starts where that serializer starts — the content region — and\n * skips what it skips, through the same chrome predicate: a heading in the\n * site's navigation is not the page's heading.\n */\n\n/** How much of a page's text a prompt carries. A listing summarizes; it does not need the page. */\nexport const AI_SEO_PAGE_TEXT_MAX_CHARS = 2_000\n\n/** How much text around an image describes it. */\nconst IMAGE_CONTEXT_MAX_CHARS = 160\n\n/** The deepest a walk goes, and it stops at a cycle — a node map is data. */\nconst MAX_DEPTH = 64\n\n/** The text components whose heading level `headingLevelOf` reads. */\nconst TEXT_COMPONENTS: ReadonlySet<string> = new Set(['muiTypography', 'muiInlineText', 'muiListItemText'])\n\nexport interface AiSeoHeading {\n /** The element, or `null` for a heading inside rich text a fix cannot address. */\n nodeId: string | null\n level: number\n text: string\n /** Whether a content fix can change it: a text component with plain text. */\n editable: boolean\n}\n\nexport interface AiSeoImage {\n nodeId: string\n src: string\n alt: string\n /** The nearest heading or text before it, which is what the image illustrates. */\n context: string\n}\n\nexport interface AiSeoPageFacts {\n /** The page's Markdown body, capped. */\n text: string\n wordCount: number\n headings: AiSeoHeading[]\n h1s: AiSeoHeading[]\n images: AiSeoImage[]\n imagesMissingAlt: AiSeoImage[]\n /** The node the content region starts at; a new heading goes first inside it. */\n contentRootId: string | null\n}\n\nconst collapse = (value: string): string => value.replace(/\\s+/g, ' ').trim()\n\n/** Author HTML to its text, for a heading stored as rich text. */\nfunction htmlText(html: string): string {\n return collapse(html.replace(/<[^>]*>/g, ' ').replace(/ /g, ' ').replace(/&/g, '&'))\n}\n\n/** A text component's own text, and whether it is plain (a fix can rewrite it). */\nfunction textOf(props: Record<string, unknown>): { text: string; plain: boolean } {\n const html = props['html']\n if (typeof html === 'string' && html.trim()) return { text: htmlText(html), plain: false }\n const children = props['children']\n return typeof children === 'string' ? { text: collapse(children), plain: true } : { text: '', plain: true }\n}\n\nconst childIdsOf = (node: PageMarkdownNode | undefined): string[] =>\n Array.isArray(node?.nodes) ? (node?.nodes as unknown[]).filter((id): id is string => typeof id === 'string') : []\n\n/** Read the facts a listing and an audit need from one page's node map. */\nexport function aiSeoPageFacts(\n nodes: PageMarkdownNodes | null | undefined,\n options: { rootId?: string; maxChars?: number } = {},\n): AiSeoPageFacts {\n const empty: AiSeoPageFacts = {\n text: '',\n wordCount: 0,\n headings: [],\n h1s: [],\n images: [],\n imagesMissingAlt: [],\n contentRootId: null,\n }\n if (!nodes) return empty\n const contentRootId = pageContentRootId(nodes, options.rootId)\n if (!contentRootId) return empty\n\n const markdown = buildPageMarkdown({ nodes, rootId: options.rootId })\n const text = markdown.trim().slice(0, options.maxChars ?? AI_SEO_PAGE_TEXT_MAX_CHARS)\n const headings: AiSeoHeading[] = []\n const images: AiSeoImage[] = []\n let lastText = ''\n\n const seen = new Set<string>()\n const visit = (id: string, depth: number): void => {\n if (depth > MAX_DEPTH || seen.has(id)) return\n const node = nodes[id]\n if (!node) return\n seen.add(id)\n if (isPageChromeNode(node)) return\n const props = (node.props ?? {}) as Record<string, unknown>\n const componentId = String(node.componentId ?? '')\n if (TEXT_COMPONENTS.has(componentId)) {\n const { text: own, plain } = textOf(props)\n const level = headingLevelOf(props)\n if (own) {\n if (level > 0) headings.push({ nodeId: id, level, text: own, editable: plain })\n lastText = own\n }\n } else if (componentId === 'markdown' && typeof props['content'] === 'string') {\n for (const line of String(props['content']).split('\\n')) {\n const match = /^(#{1,6})\\s+(.+)$/.exec(line.trim())\n if (match) {\n headings.push({ nodeId: null, level: match[1].length, text: collapse(match[2]), editable: false })\n }\n }\n lastText = collapse(String(props['content'])).slice(0, IMAGE_CONTEXT_MAX_CHARS)\n } else if (componentId === 'custom-html' && typeof (props['html'] ?? props['children']) === 'string') {\n const html = String(props['html'] ?? props['children'])\n for (const match of html.matchAll(/<h([1-6])[^>]*>([\\s\\S]*?)<\\/h\\1>/gi)) {\n headings.push({ nodeId: null, level: Number(match[1]), text: htmlText(match[2]), editable: false })\n }\n } else if (componentId === 'image') {\n const src = String(props['src'] ?? '').trim()\n if (src) {\n images.push({\n nodeId: id,\n src,\n alt: collapse(String(props['alt'] ?? '')),\n context: lastText.slice(0, IMAGE_CONTEXT_MAX_CHARS),\n })\n }\n }\n for (const childId of childIdsOf(node)) visit(childId, depth + 1)\n }\n visit(contentRootId, 0)\n\n return {\n text,\n wordCount: text ? text.split(/\\s+/).filter(Boolean).length : 0,\n headings,\n h1s: headings.filter((heading) => heading.level === 1),\n images,\n imagesMissingAlt: images.filter((image) => !image.alt),\n contentRootId,\n }\n}\n"],"names":["buildPageMarkdown","headingLevelOf","isPageChromeNode","pageContentRootId","AI_SEO_PAGE_TEXT_MAX_CHARS","IMAGE_CONTEXT_MAX_CHARS","MAX_DEPTH","TEXT_COMPONENTS","Set","collapse","value","replace","trim","htmlText","html","textOf","props","text","plain","children","childIdsOf","node","Array","isArray","nodes","filter","id","aiSeoPageFacts","options","empty","wordCount","headings","h1s","images","imagesMissingAlt","contentRootId","rootId","markdown","slice","maxChars","lastText","seen","visit","depth","has","add","componentId","String","own","level","push","nodeId","editable","line","split","match","exec","length","matchAll","Number","src","alt","context","childId","Boolean","heading","image"],"mappings":"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"}
|