create-ailk 0.5.0 → 0.6.1
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 +55 -8
- package/component-catalog.json +336 -27
- package/component-catalog.md +25 -12
- package/dist/cli.js +54 -5
- package/dist/copy.d.ts +14 -0
- package/dist/copy.js +38 -1
- package/dist/lib/api-boot-assertion.d.ts +46 -0
- package/dist/lib/api-boot-assertion.js +70 -0
- package/dist/lib/extract-module-bundle.d.ts +20 -1
- package/dist/lib/extract-module-bundle.js +64 -5
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/lib/module-license-gate.d.ts +6 -1
- package/dist/lib/module-license-gate.js +6 -1
- package/dist/lib/pro-callout.js +1 -1
- package/dist/module-architecture.d.ts +1 -0
- package/dist/module-architecture.js +253 -127
- package/dist/modules/commerce/0.6.0.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.0.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.0.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.0.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.0.tar.gz +0 -0
- package/dist/modules/workspace/0.6.0.tar.gz +0 -0
- package/dist/programmatic.js +14 -10
- package/dist/surfaces.js +105 -7
- package/dist/template-manifest.d.ts +33 -0
- package/dist/template-manifest.js +99 -16
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +1 -0
- package/package.json +3 -2
- package/site-templates/marketing-site/DESIGN.md +148 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +22 -2
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +9 -2
- package/site-templates/marketing-site/ailk-template.json +1 -1
- package/site-templates/marketing-site-free/DESIGN.md +148 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +2 -2
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +1 -0
- package/site-templates/minimalism-site/DESIGN.md +148 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +10 -2
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +47 -35
- package/site-templates/minimalism-site/ailk-template.json +2 -1
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +49 -19
- package/site-templates/saas-product/DESIGN.md +148 -0
- package/site-templates/simple-service/DESIGN.md +148 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
- package/site-templates/simple-service/ailk-template.json +10 -0
- package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
- package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
- package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/simple-service/provenance.json +4 -0
- package/templates/.env.example +72 -7
- package/templates/CLAUDE.md +2 -3
- package/templates/DESIGN.md +155 -0
- package/templates/README.md +42 -8
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
- package/templates/apps/api/.env.example +13 -0
- package/templates/apps/api/CLAUDE.md +26 -2
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +83 -7
- package/templates/apps/api/src/__tests__/server.test.ts +471 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +2 -0
- package/templates/apps/api/src/config/index.ts +62 -0
- package/templates/apps/api/src/config/modules.ts +7 -3
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +238 -2
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
- package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
- package/templates/apps/api/src/lib/content-adapter.ts +57 -31
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
- package/templates/apps/api/src/lib/error-serializer.ts +23 -3
- package/templates/apps/api/src/lib/outbound-http.ts +7 -2
- package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
- package/templates/apps/api/src/lib/site.ts +208 -9
- package/templates/apps/api/src/lib/stripe.ts +66 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +182 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +42 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
- package/templates/apps/api/src/openapi/spec.ts +543 -2
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
- package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
- package/templates/apps/api/src/routes/auth/user.ts +61 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
- package/templates/apps/api/src/routes/checkout/index.ts +34 -0
- package/templates/apps/api/src/routes/checkout/session.ts +139 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
- package/templates/apps/api/src/routes/content/get-page.ts +94 -12
- package/templates/apps/api/src/routes/content/index.ts +13 -73
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
- package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
- package/templates/apps/api/src/routes/content/search.ts +129 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
- package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
- package/templates/apps/api/src/routes/requests/index.ts +54 -0
- package/templates/apps/api/src/routes/webhooks/README.md +265 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
- package/templates/apps/api/src/server.ts +439 -49
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
- package/templates/apps/api/src/services/abandon-email.ts +8 -2
- package/templates/apps/api/src/services/aeo-score.ts +49 -12
- package/templates/apps/api/src/services/audit-log.ts +668 -0
- package/templates/apps/api/src/services/consent.ts +18 -0
- package/templates/apps/api/src/services/default-tenant.ts +70 -31
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
- package/templates/apps/api/src/services/dunning-policy.ts +294 -0
- package/templates/apps/api/src/services/entitlements.ts +80 -0
- package/templates/apps/api/src/services/lead-promotion.ts +13 -14
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +23 -2
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/project-listings.ts +201 -6
- package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
- package/templates/apps/api/src/services/request-confirm.ts +646 -0
- package/templates/apps/api/src/services/revalidation.ts +173 -0
- package/templates/apps/api/src/services/verified-email.ts +71 -0
- package/templates/apps/api/src/vercel-handler.ts +75 -35
- package/templates/apps/mcp/.env.example +17 -0
- package/templates/apps/mcp/CLAUDE.md +57 -24
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
- package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
- package/templates/apps/mcp/__tests__/index.test.ts +39 -0
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
- package/templates/apps/mcp/__tests__/parity.test.ts +10 -7
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
- package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
- package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
- package/templates/apps/mcp/__tests__/server.test.ts +67 -14
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +2 -0
- package/templates/apps/mcp/src/abuse/index.ts +159 -24
- package/templates/apps/mcp/src/config/modules.ts +10 -5
- package/templates/apps/mcp/src/http-transport.ts +102 -29
- package/templates/apps/mcp/src/index.ts +73 -11
- package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
- package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
- package/templates/apps/mcp/src/lib/prisma.ts +39 -0
- package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
- package/templates/apps/mcp/src/server.ts +59 -35
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
- package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
- package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
- package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
- package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
- package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
- package/templates/apps/mcp/src/tools/index.ts +6 -4
- package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
- package/templates/apps/mcp/src/tools/purchase.ts +128 -45
- package/templates/apps/mcp/src/tools/read_content.ts +20 -6
- package/templates/apps/mcp/src/tools/search_content.ts +11 -4
- package/templates/apps/web/.env.example +71 -5
- package/templates/apps/web/CLAUDE.md +44 -0
- package/templates/apps/web/PRODUCT.md +86 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
- package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +63 -8
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +18 -1
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/layout.tsx +133 -87
- package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
- package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
- package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
- package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
- package/templates/apps/web/app/api/revalidate/route.ts +194 -0
- package/templates/apps/web/auth.ts +407 -8
- package/templates/apps/web/jest.config.cjs +8 -4
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +135 -20
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +18 -1
- package/templates/apps/web/lib/auth-email.ts +318 -0
- package/templates/apps/web/lib/auth-env.ts +85 -0
- package/templates/apps/web/lib/auth-routes.ts +55 -0
- package/templates/apps/web/lib/meeting-slugs.ts +53 -0
- package/templates/apps/web/lib/site-brand.tsx +4 -8
- package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
- package/templates/apps/web/messages/en.json +187 -0
- package/templates/apps/web/middleware.ts +1 -1
- package/templates/apps/web/package.json +2 -1
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +93 -0
- package/templates/content/_site.mdx +18 -3
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
- package/templates/database/CHANGELOG.md +149 -0
- package/templates/database/README.md +10 -3
- package/templates/database/__tests__/appointment.db.test.ts +106 -0
- package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
- package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
- package/templates/database/__tests__/tenancy.test.ts +141 -0
- package/templates/database/config/schema.prisma +79 -2
- package/templates/database/content/schema.prisma +87 -11
- package/templates/database/inbox/schema.prisma +452 -9
- package/templates/database/jest.config.cjs +5 -3
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
- package/templates/database/package.json +3 -2
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/versions.json +34 -34
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
- package/templates/apps/api/src/routes/content/create.ts +0 -115
- package/templates/apps/api/src/routes/content/delete.ts +0 -82
- package/templates/apps/api/src/routes/content/update.ts +0 -112
- package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
- package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
- package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
- package/templates/apps/mcp/src/tools/create_page.ts +0 -96
- package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
- package/templates/apps/mcp/src/tools/update_page.ts +0 -92
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
- package/templates/apps/web/public/logo-dark.svg +0 -21
- package/templates/apps/web/public/logo-light.svg +0 -21
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
|
@@ -307,6 +307,45 @@ describe("runAbandonEmailSweep", () => {
|
|
|
307
307
|
expect(call[0].fromAddress).toBe("owner-noreply@example.com");
|
|
308
308
|
});
|
|
309
309
|
|
|
310
|
+
// ── #4740 — an empty-string override is an env gap, not a sender ─────────
|
|
311
|
+
|
|
312
|
+
it("falls back to AILK_LEAD_NOTIFICATION_FROM when AILK_ABANDON_EMAIL_FROM is set to an empty string", async () => {
|
|
313
|
+
process.env.AILK_ABANDON_EMAIL_FROM = "";
|
|
314
|
+
process.env.AILK_LEAD_NOTIFICATION_FROM = "owner-noreply@example.com";
|
|
315
|
+
(prisma.flowDefinition.findMany as jest.Mock).mockResolvedValue([
|
|
316
|
+
flowDefRow(),
|
|
317
|
+
]);
|
|
318
|
+
(prisma.flowSession.findMany as jest.Mock).mockResolvedValue([
|
|
319
|
+
sessionRow(),
|
|
320
|
+
]);
|
|
321
|
+
(prisma.flowSession.update as jest.Mock).mockResolvedValue({});
|
|
322
|
+
|
|
323
|
+
await runAbandonEmailSweep({ now: NOW });
|
|
324
|
+
|
|
325
|
+
const [call] = mockSendAbandonEmail.mock.calls;
|
|
326
|
+
expect(call[0].fromAddress).toBe("owner-noreply@example.com");
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("leaves an enabled candidate UNSTAMPED when BOTH sender env vars are empty strings", async () => {
|
|
330
|
+
process.env.AILK_ABANDON_EMAIL_FROM = "";
|
|
331
|
+
process.env.AILK_LEAD_NOTIFICATION_FROM = "";
|
|
332
|
+
(prisma.flowDefinition.findMany as jest.Mock).mockResolvedValue([
|
|
333
|
+
flowDefRow(),
|
|
334
|
+
]);
|
|
335
|
+
(prisma.flowSession.findMany as jest.Mock).mockResolvedValue([
|
|
336
|
+
sessionRow(),
|
|
337
|
+
]);
|
|
338
|
+
|
|
339
|
+
const result = await runAbandonEmailSweep({ now: NOW });
|
|
340
|
+
|
|
341
|
+
expect(mockSendAbandonEmail).not.toHaveBeenCalled();
|
|
342
|
+
expect(prisma.flowSession.update).not.toHaveBeenCalled();
|
|
343
|
+
expect(result).toMatchObject({
|
|
344
|
+
ok: true,
|
|
345
|
+
summary: { selected: 1, sent: 0, dark: 0, failed: 0, skipped: 1 },
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
310
349
|
// ── AC3 — locale resolution ──────────────────────────────────────────────
|
|
311
350
|
|
|
312
351
|
it("resolves copy at the session's persisted locale", async () => {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* fixture loading (AC1, AC3-AC7).
|
|
6
6
|
*/
|
|
7
7
|
import {
|
|
8
|
+
AEO_SCORE_EXCLUDED_READINESS_RULES,
|
|
8
9
|
AEO_SCORE_RULES,
|
|
9
10
|
AEO_DIMENSION_SEVERITY,
|
|
10
11
|
AEO_SCORE_DEFAULT_BUDGET_MS,
|
|
@@ -14,11 +15,17 @@ import {
|
|
|
14
15
|
scoreUrl,
|
|
15
16
|
loadExampleScore,
|
|
16
17
|
} from "../aeo-score";
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
aeoDimensionSchema,
|
|
20
|
+
aeoScoreResultSchema,
|
|
21
|
+
} from "@working-theory/validation";
|
|
18
22
|
import type { CrawlResult } from "@working-theory/aeo/fetch";
|
|
19
23
|
|
|
20
24
|
/** Typed as the platform signature so it assigns to `ScoreUrlDeps.fetchImpl` with no cast. */
|
|
21
|
-
type FetchStub = jest.Mock<
|
|
25
|
+
type FetchStub = jest.Mock<
|
|
26
|
+
Promise<Response>,
|
|
27
|
+
[RequestInfo | URL, RequestInit?]
|
|
28
|
+
>;
|
|
22
29
|
|
|
23
30
|
import expectedResult from "../../../../../packages/aeo/__tests__/fixtures/example-score/result.json";
|
|
24
31
|
|
|
@@ -28,7 +35,13 @@ function page(
|
|
|
28
35
|
expectedType: string,
|
|
29
36
|
jsonLd: unknown[],
|
|
30
37
|
): CrawlResult["pages"][number] {
|
|
31
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
path,
|
|
40
|
+
type,
|
|
41
|
+
expectedType,
|
|
42
|
+
url: `https://example.com${path}`,
|
|
43
|
+
jsonLd,
|
|
44
|
+
};
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
/**
|
|
@@ -59,7 +72,9 @@ describe("AEO_SCORE_RULES", () => {
|
|
|
59
72
|
]);
|
|
60
73
|
expect(AEO_SCORE_RULES.map((r) => r.id)).not.toContain("always-passes");
|
|
61
74
|
expect(AEO_SCORE_RULES.map((r) => r.id)).not.toContain("always-fails");
|
|
62
|
-
expect(AEO_SCORE_RULES.map((r) => r.id)).not.toContain(
|
|
75
|
+
expect(AEO_SCORE_RULES.map((r) => r.id)).not.toContain(
|
|
76
|
+
"schema-correctness",
|
|
77
|
+
);
|
|
63
78
|
});
|
|
64
79
|
|
|
65
80
|
it("matches the prospect-audit-site severity table verbatim", () => {
|
|
@@ -83,22 +98,32 @@ describe("resolveScoreBudgetMs", () => {
|
|
|
83
98
|
|
|
84
99
|
it("falls back to the default and logs when above the 15000ms cap", () => {
|
|
85
100
|
const warn = jest.fn();
|
|
86
|
-
expect(resolveScoreBudgetMs("20000", { warn })).toBe(
|
|
101
|
+
expect(resolveScoreBudgetMs("20000", { warn })).toBe(
|
|
102
|
+
AEO_SCORE_DEFAULT_BUDGET_MS,
|
|
103
|
+
);
|
|
87
104
|
expect(warn).toHaveBeenCalledTimes(1);
|
|
88
105
|
expect(warn.mock.calls[0]?.[0]).toContain(String(AEO_SCORE_MAX_BUDGET_MS));
|
|
89
106
|
});
|
|
90
107
|
|
|
91
108
|
it("falls back silently on an unparseable/non-positive value", () => {
|
|
92
109
|
const warn = jest.fn();
|
|
93
|
-
expect(resolveScoreBudgetMs("not-a-number", { warn })).toBe(
|
|
94
|
-
|
|
110
|
+
expect(resolveScoreBudgetMs("not-a-number", { warn })).toBe(
|
|
111
|
+
AEO_SCORE_DEFAULT_BUDGET_MS,
|
|
112
|
+
);
|
|
113
|
+
expect(resolveScoreBudgetMs("-5", { warn })).toBe(
|
|
114
|
+
AEO_SCORE_DEFAULT_BUDGET_MS,
|
|
115
|
+
);
|
|
95
116
|
expect(warn).not.toHaveBeenCalled();
|
|
96
117
|
});
|
|
97
118
|
});
|
|
98
119
|
|
|
99
120
|
describe("scoreCrawl", () => {
|
|
100
121
|
it("returns unavailable/no_pages_fetched for an empty crawl", () => {
|
|
101
|
-
const crawl: CrawlResult = {
|
|
122
|
+
const crawl: CrawlResult = {
|
|
123
|
+
base: "https://example.com",
|
|
124
|
+
pages: [],
|
|
125
|
+
skipped: [],
|
|
126
|
+
};
|
|
102
127
|
const result = scoreCrawl(crawl);
|
|
103
128
|
expect(result).toEqual({
|
|
104
129
|
status: "unavailable",
|
|
@@ -150,8 +175,18 @@ describe("scoreCrawl", () => {
|
|
|
150
175
|
it("sets meta.partial true when a page was skipped for budget_exceeded", () => {
|
|
151
176
|
const crawl: CrawlResult = {
|
|
152
177
|
base: "https://example.com",
|
|
153
|
-
pages: [
|
|
154
|
-
|
|
178
|
+
pages: [
|
|
179
|
+
page("/", "homepage", "HomePage", [
|
|
180
|
+
{ "@type": "HomePage", name: "Home" },
|
|
181
|
+
]),
|
|
182
|
+
],
|
|
183
|
+
skipped: [
|
|
184
|
+
{
|
|
185
|
+
path: "/about",
|
|
186
|
+
url: "https://example.com/about",
|
|
187
|
+
reason: "budget_exceeded",
|
|
188
|
+
},
|
|
189
|
+
],
|
|
155
190
|
};
|
|
156
191
|
const result = scoreCrawl(crawl);
|
|
157
192
|
if (result.status !== "ready") throw new Error("expected ready");
|
|
@@ -162,7 +197,11 @@ describe("scoreCrawl", () => {
|
|
|
162
197
|
it("result satisfies aeoScoreResultSchema (AC7)", () => {
|
|
163
198
|
const crawl: CrawlResult = {
|
|
164
199
|
base: "https://example.com",
|
|
165
|
-
pages: [
|
|
200
|
+
pages: [
|
|
201
|
+
page("/", "homepage", "HomePage", [
|
|
202
|
+
{ "@type": "HomePage", name: "Home" },
|
|
203
|
+
]),
|
|
204
|
+
],
|
|
166
205
|
skipped: [],
|
|
167
206
|
};
|
|
168
207
|
const result = scoreCrawl(crawl);
|
|
@@ -187,12 +226,17 @@ describe("scoreCrawl", () => {
|
|
|
187
226
|
it("redacts a raw assertUrlSafe rejection reason to the ssrf_blocked sentinel", () => {
|
|
188
227
|
const crawl: CrawlResult = {
|
|
189
228
|
base: "https://example.com",
|
|
190
|
-
pages: [
|
|
229
|
+
pages: [
|
|
230
|
+
page("/", "homepage", "HomePage", [
|
|
231
|
+
{ "@type": "HomePage", name: "Home" },
|
|
232
|
+
]),
|
|
233
|
+
],
|
|
191
234
|
skipped: [
|
|
192
235
|
{
|
|
193
236
|
path: "/admin",
|
|
194
237
|
url: "http://169.254.169.254/admin",
|
|
195
|
-
reason:
|
|
238
|
+
reason:
|
|
239
|
+
"Host 169.254.169.254 resolves to a blocked address: 169.254.169.254",
|
|
196
240
|
},
|
|
197
241
|
],
|
|
198
242
|
};
|
|
@@ -201,38 +245,57 @@ describe("scoreCrawl", () => {
|
|
|
201
245
|
expect(result.meta.skipped).toHaveLength(1);
|
|
202
246
|
expect(result.meta.skipped[0]?.reason).toBe("ssrf_blocked");
|
|
203
247
|
expect(result.meta.skipped[0]?.reason).not.toMatch(/169\.254\.169\.254/);
|
|
204
|
-
expect(result.meta.skipped[0]?.reason).not.toMatch(
|
|
248
|
+
expect(result.meta.skipped[0]?.reason).not.toMatch(
|
|
249
|
+
/resolves to a blocked address/i,
|
|
250
|
+
);
|
|
205
251
|
});
|
|
206
252
|
|
|
207
253
|
it("(AC-2) logs the raw per-path rejection detail server-side before redacting it", () => {
|
|
208
254
|
const crawl: CrawlResult = {
|
|
209
255
|
base: "https://example.com",
|
|
210
|
-
pages: [
|
|
256
|
+
pages: [
|
|
257
|
+
page("/", "homepage", "HomePage", [
|
|
258
|
+
{ "@type": "HomePage", name: "Home" },
|
|
259
|
+
]),
|
|
260
|
+
],
|
|
211
261
|
skipped: [
|
|
212
262
|
{
|
|
213
263
|
path: "/admin",
|
|
214
264
|
url: "http://169.254.169.254/admin",
|
|
215
|
-
reason:
|
|
265
|
+
reason:
|
|
266
|
+
"Host 169.254.169.254 resolves to a blocked address: 169.254.169.254",
|
|
216
267
|
},
|
|
217
268
|
],
|
|
218
269
|
};
|
|
219
270
|
scoreCrawl(crawl);
|
|
220
271
|
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
221
|
-
const [message, meta] = warnSpy.mock.calls[0] as [
|
|
272
|
+
const [message, meta] = warnSpy.mock.calls[0] as [
|
|
273
|
+
string,
|
|
274
|
+
Record<string, unknown>,
|
|
275
|
+
];
|
|
222
276
|
expect(message).toMatch(/aeo-score/);
|
|
223
277
|
expect(meta).toMatchObject({
|
|
224
278
|
path: "/admin",
|
|
225
279
|
url: "http://169.254.169.254/admin",
|
|
226
|
-
reason:
|
|
280
|
+
reason:
|
|
281
|
+
"Host 169.254.169.254 resolves to a blocked address: 169.254.169.254",
|
|
227
282
|
});
|
|
228
283
|
});
|
|
229
284
|
|
|
230
285
|
it("leaves a non-SSRF skip reason (budget_exceeded, an HTTP failure) unchanged", () => {
|
|
231
286
|
const crawl: CrawlResult = {
|
|
232
287
|
base: "https://example.com",
|
|
233
|
-
pages: [
|
|
288
|
+
pages: [
|
|
289
|
+
page("/", "homepage", "HomePage", [
|
|
290
|
+
{ "@type": "HomePage", name: "Home" },
|
|
291
|
+
]),
|
|
292
|
+
],
|
|
234
293
|
skipped: [
|
|
235
|
-
{
|
|
294
|
+
{
|
|
295
|
+
path: "/about",
|
|
296
|
+
url: "https://example.com/about",
|
|
297
|
+
reason: "budget_exceeded",
|
|
298
|
+
},
|
|
236
299
|
{
|
|
237
300
|
path: "/blog",
|
|
238
301
|
url: "https://example.com/blog",
|
|
@@ -243,7 +306,11 @@ describe("scoreCrawl", () => {
|
|
|
243
306
|
const result = scoreCrawl(crawl);
|
|
244
307
|
if (result.status !== "ready") throw new Error("expected ready");
|
|
245
308
|
expect(result.meta.skipped).toEqual([
|
|
246
|
-
{
|
|
309
|
+
{
|
|
310
|
+
path: "/about",
|
|
311
|
+
url: "https://example.com/about",
|
|
312
|
+
reason: "budget_exceeded",
|
|
313
|
+
},
|
|
247
314
|
{
|
|
248
315
|
path: "/blog",
|
|
249
316
|
url: "https://example.com/blog",
|
|
@@ -272,7 +339,10 @@ describe("scoreCrawl", () => {
|
|
|
272
339
|
),
|
|
273
340
|
);
|
|
274
341
|
|
|
275
|
-
const result = await scoreUrl("https://example.com", {
|
|
342
|
+
const result = await scoreUrl("https://example.com", {
|
|
343
|
+
fetchImpl,
|
|
344
|
+
resolver,
|
|
345
|
+
});
|
|
276
346
|
|
|
277
347
|
if (result.status !== "ready") {
|
|
278
348
|
throw new Error(`expected ready, got ${JSON.stringify(result)}`);
|
|
@@ -294,14 +364,68 @@ describe("aeoScoreResultSchema (AC7)", () => {
|
|
|
294
364
|
status: "ready",
|
|
295
365
|
score: 50,
|
|
296
366
|
dimensions: [
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
367
|
+
{
|
|
368
|
+
id: "schema-coverage",
|
|
369
|
+
label: "x",
|
|
370
|
+
weight: 10,
|
|
371
|
+
score: 50,
|
|
372
|
+
severity: "high",
|
|
373
|
+
finding: null,
|
|
374
|
+
pagesPassing: 1,
|
|
375
|
+
pagesTotal: 2,
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: "jsonld-validity",
|
|
379
|
+
label: "x",
|
|
380
|
+
weight: 10,
|
|
381
|
+
score: 50,
|
|
382
|
+
severity: "medium",
|
|
383
|
+
finding: null,
|
|
384
|
+
pagesPassing: 1,
|
|
385
|
+
pagesTotal: 2,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: "entity-markup",
|
|
389
|
+
label: "x",
|
|
390
|
+
weight: 1,
|
|
391
|
+
score: 50,
|
|
392
|
+
severity: "high",
|
|
393
|
+
finding: null,
|
|
394
|
+
pagesPassing: 1,
|
|
395
|
+
pagesTotal: 2,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "faq-howto-incentive",
|
|
399
|
+
label: "x",
|
|
400
|
+
weight: 0,
|
|
401
|
+
score: 50,
|
|
402
|
+
severity: "low",
|
|
403
|
+
finding: null,
|
|
404
|
+
pagesPassing: 1,
|
|
405
|
+
pagesTotal: 2,
|
|
406
|
+
},
|
|
301
407
|
],
|
|
302
408
|
lowestTwo: [
|
|
303
|
-
{
|
|
304
|
-
|
|
409
|
+
{
|
|
410
|
+
id: "schema-coverage",
|
|
411
|
+
label: "x",
|
|
412
|
+
weight: 10,
|
|
413
|
+
score: 50,
|
|
414
|
+
severity: "high",
|
|
415
|
+
finding: null,
|
|
416
|
+
pagesPassing: 1,
|
|
417
|
+
pagesTotal: 2,
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "entity-markup",
|
|
421
|
+
label: "x",
|
|
422
|
+
weight: 1,
|
|
423
|
+
score: 50,
|
|
424
|
+
severity: "high",
|
|
425
|
+
finding: null,
|
|
426
|
+
pagesPassing: 1,
|
|
427
|
+
pagesTotal: 2,
|
|
428
|
+
},
|
|
305
429
|
],
|
|
306
430
|
meta: {
|
|
307
431
|
auditedAt: new Date().toISOString(),
|
|
@@ -314,11 +438,21 @@ describe("aeoScoreResultSchema (AC7)", () => {
|
|
|
314
438
|
}),
|
|
315
439
|
).not.toThrow();
|
|
316
440
|
|
|
317
|
-
expect(() =>
|
|
441
|
+
expect(() =>
|
|
442
|
+
aeoScoreResultSchema.parse({ status: "computing" }),
|
|
443
|
+
).not.toThrow();
|
|
318
444
|
|
|
319
|
-
for (const reason of [
|
|
445
|
+
for (const reason of [
|
|
446
|
+
"ssrf_blocked",
|
|
447
|
+
"no_pages_fetched",
|
|
448
|
+
"crawl_failed",
|
|
449
|
+
] as const) {
|
|
320
450
|
expect(() =>
|
|
321
|
-
aeoScoreResultSchema.parse({
|
|
451
|
+
aeoScoreResultSchema.parse({
|
|
452
|
+
status: "unavailable",
|
|
453
|
+
reason,
|
|
454
|
+
message: "x",
|
|
455
|
+
}),
|
|
322
456
|
).not.toThrow();
|
|
323
457
|
}
|
|
324
458
|
});
|
|
@@ -348,7 +482,9 @@ describe("scoreUrl", () => {
|
|
|
348
482
|
it("(AC1) issues the five default paths concurrently — a 2s-delay fetchImpl completes well under 10s", async () => {
|
|
349
483
|
const fetchImpl: FetchStub = jest.fn(async () => {
|
|
350
484
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
351
|
-
return new Response(
|
|
485
|
+
return new Response(
|
|
486
|
+
'<html><script type="application/ld+json">{"@type":"HomePage","name":"x"}</script></html>',
|
|
487
|
+
);
|
|
352
488
|
});
|
|
353
489
|
|
|
354
490
|
const t0 = Date.now();
|
|
@@ -363,7 +499,9 @@ describe("scoreUrl", () => {
|
|
|
363
499
|
}, 10_000);
|
|
364
500
|
|
|
365
501
|
it("(AC1) with budgetMs 500 and a 2s-delay fetchImpl, returns quickly as unavailable/no_pages_fetched", async () => {
|
|
366
|
-
const fetchImpl: FetchStub = jest.fn(
|
|
502
|
+
const fetchImpl: FetchStub = jest.fn(
|
|
503
|
+
delayedResponse(2000, "<html></html>"),
|
|
504
|
+
);
|
|
367
505
|
|
|
368
506
|
const t0 = Date.now();
|
|
369
507
|
const result = await scoreUrl("https://example.com", {
|
|
@@ -387,13 +525,15 @@ describe("scoreUrl", () => {
|
|
|
387
525
|
it("(AC1) one fast + four slow pages returns ready with meta.partial true and budget_exceeded skips", async () => {
|
|
388
526
|
const homeResponse = delayedResponse(
|
|
389
527
|
0,
|
|
390
|
-
|
|
528
|
+
'<html><script type="application/ld+json">{"@type":"HomePage","name":"x"}</script></html>',
|
|
391
529
|
);
|
|
392
530
|
const slowResponse = delayedResponse(5000, "<html></html>");
|
|
393
|
-
const fetchImpl: FetchStub = jest.fn(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
531
|
+
const fetchImpl: FetchStub = jest.fn(
|
|
532
|
+
(input: RequestInfo | URL, init?: RequestInit) => {
|
|
533
|
+
const isHome = String(input).endsWith("example.com/");
|
|
534
|
+
return isHome ? homeResponse(input, init) : slowResponse(input, init);
|
|
535
|
+
},
|
|
536
|
+
);
|
|
397
537
|
|
|
398
538
|
const result = await scoreUrl("https://example.com", {
|
|
399
539
|
fetchImpl,
|
|
@@ -401,10 +541,13 @@ describe("scoreUrl", () => {
|
|
|
401
541
|
budgetMs: 800,
|
|
402
542
|
});
|
|
403
543
|
|
|
404
|
-
if (result.status !== "ready")
|
|
544
|
+
if (result.status !== "ready")
|
|
545
|
+
throw new Error(`expected ready, got ${JSON.stringify(result)}`);
|
|
405
546
|
expect(result.meta.partial).toBe(true);
|
|
406
547
|
// Spec AC1: one fast + FOUR slow pages — all four abort at the budget.
|
|
407
|
-
expect(
|
|
548
|
+
expect(
|
|
549
|
+
result.meta.skipped.filter((s) => s.reason === "budget_exceeded").length,
|
|
550
|
+
).toBe(4);
|
|
408
551
|
expect(result.meta.pagesAudited).toBe(1);
|
|
409
552
|
}, 10_000);
|
|
410
553
|
|
|
@@ -473,3 +616,37 @@ describe("loadExampleScore (AC6, D6)", () => {
|
|
|
473
616
|
}).toEqual(expectedResult);
|
|
474
617
|
});
|
|
475
618
|
});
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* #5949 D6/FR11 — the readiness rules are deliberately NOT in this service's
|
|
622
|
+
* rule set. They grade frontmatter and site-set facts, and `buildAuditPayload`
|
|
623
|
+
* produces no frontmatter, so on a crawl input all three would pass vacuously:
|
|
624
|
+
* three always-100 dimensions in a closed four-member public enum.
|
|
625
|
+
*
|
|
626
|
+
* `/v1/aeo/score` has no MCP twin (apps/api/CLAUDE.md § API ↔ MCP parity), so
|
|
627
|
+
* the skip is not a parity divergence — and these assertions are the "stated
|
|
628
|
+
* and tested" half AC-3 asks for, failing loudly if either side drifts.
|
|
629
|
+
*/
|
|
630
|
+
describe("readiness-rule exclusion (#5949 D6)", () => {
|
|
631
|
+
it("AEO_SCORE_RULES carries none of the excluded readiness rules", () => {
|
|
632
|
+
const ids = AEO_SCORE_RULES.map((r) => r.id);
|
|
633
|
+
for (const excluded of AEO_SCORE_EXCLUDED_READINESS_RULES) {
|
|
634
|
+
expect(ids).not.toContain(excluded);
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it("the public dimension enum is still exactly the four content rules", () => {
|
|
639
|
+
expect([...aeoDimensionSchema.shape.id.options].sort()).toEqual([
|
|
640
|
+
"entity-markup",
|
|
641
|
+
"faq-howto-incentive",
|
|
642
|
+
"jsonld-validity",
|
|
643
|
+
"schema-coverage",
|
|
644
|
+
]);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it("AEO_SCORE_RULES' ids are exactly the dimension enum's options", () => {
|
|
648
|
+
expect(AEO_SCORE_RULES.map((r) => r.id).sort()).toEqual(
|
|
649
|
+
[...aeoDimensionSchema.shape.id.options].sort(),
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
});
|