create-ailk 0.4.0 → 0.6.0
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 +12248 -0
- package/component-catalog.md +193 -68
- package/dist/cli.js +29 -1
- package/dist/derive-architecture.js +5 -2
- 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 +50 -2
- package/dist/lib/extract-module-bundle.js +223 -15
- package/dist/lib/fetch-guard.d.ts +28 -0
- package/dist/lib/fetch-guard.js +101 -0
- package/dist/lib/fetch-module.js +4 -0
- package/dist/lib/fetch-template.js +4 -0
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/module-architecture.d.ts +60 -8
- package/dist/module-architecture.js +587 -242
- package/dist/parse-args.js +6 -2
- package/dist/programmatic.d.ts +10 -0
- package/dist/programmatic.js +134 -19
- package/dist/site-chrome.d.ts +80 -0
- package/dist/site-chrome.js +135 -0
- package/dist/surfaces.d.ts +21 -6
- package/dist/surfaces.js +209 -36
- package/dist/template-manifest.d.ts +88 -3
- package/dist/template-manifest.js +281 -29
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +2 -0
- package/package.json +10 -2
- package/site-templates/_base/ailk-template.json +6 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
- package/site-templates/marketing-site/ailk-template.json +11 -0
- package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
- package/site-templates/marketing-site/provenance.json +4 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
- package/site-templates/marketing-site-free/ailk-template.json +2 -1
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
- package/site-templates/minimalism-site/ailk-template.json +22 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
- package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
- package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
- package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
- package/site-templates/minimalism-site/provenance.json +4 -0
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
- package/site-templates/saas-product/ailk-template.json +16 -0
- package/site-templates/saas-product/provenance.json +10 -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/.claude/skills/README.md +16 -25
- package/templates/.env.example +102 -12
- package/templates/.github/workflows/ci.yml +48 -0
- package/templates/README.md +3 -1
- 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 +29 -2
- package/templates/apps/api/eslint.config.mjs +69 -0
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +7 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
- 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 +10 -1
- 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 +241 -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 +521 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
- package/templates/apps/api/src/middleware/default-tenant.ts +136 -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/__tests__/list.test.ts +171 -0
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/leads/list.ts +104 -0
- 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 +521 -55
- 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-promotion.test.ts +408 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -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 +300 -0
- 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 +160 -0
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +284 -0
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
- 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 +58 -25
- 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 +12 -9
- 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/{scaffold.test.ts → create_project.test.ts} +23 -23
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/eslint.config.mjs +69 -0
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +8 -2
- 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 +61 -37
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
- 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 +9 -7
- 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 +74 -8
- package/templates/apps/web/CLAUDE.md +46 -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 +67 -14
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +308 -65
- 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/components/google-analytics.tsx +18 -1
- package/templates/apps/web/eslint.config.mjs +69 -0
- package/templates/apps/web/jest.config.cjs +15 -19
- 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__/structured-data.test.tsx +50 -6
- 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/nav-config.ts +27 -7
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/lib/site-chrome.ts +29 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
- package/templates/apps/web/messages/en.json +216 -2
- package/templates/apps/web/next.config.mjs +20 -0
- package/templates/apps/web/package.json +8 -3
- package/templates/apps/web/sentry.client.config.ts +131 -0
- package/templates/apps/web/sentry.edge.config.ts +72 -0
- package/templates/apps/web/sentry.server.config.ts +72 -0
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
- package/templates/apps/web/src/lib/routes.ts +13 -18
- package/templates/brand_assets/README.md +24 -0
- package/templates/content/en/AboutPage/about.mdx +160 -15
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ContactPage/contact.mdx +90 -13
- package/templates/content/en/FAQPage/faq.mdx +62 -22
- package/templates/content/en/HomePage/home.mdx +244 -20
- package/templates/content/en/LandingPage/blog.mdx +83 -0
- package/templates/content/en/LandingPage/features.mdx +103 -0
- package/templates/content/en/LandingPage/how-it-works.mdx +143 -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__/seed.test.ts +1 -1
- 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/eslint.config.mjs +69 -0
- 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 +7 -3
- package/templates/database/seed.ts +1 -1
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/templates/package.json +1 -14
- package/versions.json +52 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
- 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/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
- /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
- /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
- /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
|
@@ -16,15 +16,26 @@
|
|
|
16
16
|
* shape the inline schema parsed pre-relocation.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
mkdirSync,
|
|
21
|
+
mkdtempSync,
|
|
22
|
+
readFileSync,
|
|
23
|
+
rmSync,
|
|
24
|
+
writeFileSync,
|
|
25
|
+
} from "node:fs";
|
|
26
|
+
import { tmpdir } from "node:os";
|
|
20
27
|
import { resolve, join } from "node:path";
|
|
21
28
|
|
|
29
|
+
import { READINESS_SCAN_TYPES } from "@working-theory/aeo";
|
|
30
|
+
|
|
22
31
|
import {
|
|
23
32
|
auditInputSchema,
|
|
24
33
|
auditOutputSchema,
|
|
25
34
|
purchaseInputSchema,
|
|
26
35
|
} from "@working-theory/validation";
|
|
27
36
|
|
|
37
|
+
import { buildSinglePageAuditInput } from "../src/lib/audit-single-page.js";
|
|
38
|
+
import { readStripeTaxSignal } from "../src/lib/stripe-tax-signal.js";
|
|
28
39
|
import { ailkAudit } from "../src/tools/audit.js";
|
|
29
40
|
|
|
30
41
|
const REPO_ROOT = resolve(__dirname, "../../..");
|
|
@@ -152,10 +163,7 @@ describe("ailkAudit handler — slug input", () => {
|
|
|
152
163
|
describe("ailkAudit handler — url input (same-origin)", () => {
|
|
153
164
|
it("returns audit result for a same-origin URL", async () => {
|
|
154
165
|
const result = (await withBaseUrl(TEST_BASE_URL, () =>
|
|
155
|
-
ailkAudit(
|
|
156
|
-
{ url: `${TEST_BASE_URL}/HomePage/home` },
|
|
157
|
-
CONTENT_ROOT,
|
|
158
|
-
),
|
|
166
|
+
ailkAudit({ url: `${TEST_BASE_URL}/HomePage/home` }, CONTENT_ROOT),
|
|
159
167
|
)) as Awaited<ReturnType<typeof ailkAudit>>;
|
|
160
168
|
expect(typeof result.score).toBe("number");
|
|
161
169
|
expect(result.perPage[0].slug).toBe("home");
|
|
@@ -204,14 +212,135 @@ describe("structural guards", () => {
|
|
|
204
212
|
describe("purchaseInputSchema relocated to @working-theory/validation", () => {
|
|
205
213
|
it("parses the same shape the inline schema parsed pre-relocation", () => {
|
|
206
214
|
const result = purchaseInputSchema.safeParse({
|
|
207
|
-
|
|
215
|
+
productCode: "price_test_123",
|
|
208
216
|
quantity: 2,
|
|
209
217
|
});
|
|
210
218
|
expect(result.success).toBe(true);
|
|
211
219
|
});
|
|
212
220
|
|
|
213
|
-
it("rejects an empty
|
|
214
|
-
const result = purchaseInputSchema.safeParse({
|
|
221
|
+
it("rejects an empty productCode", () => {
|
|
222
|
+
const result = purchaseInputSchema.safeParse({ productCode: "" });
|
|
215
223
|
expect(result.success).toBe(false);
|
|
216
224
|
});
|
|
217
225
|
});
|
|
226
|
+
|
|
227
|
+
// ── #5949 — the agent-readiness rules on the MCP audit path ───────────────────
|
|
228
|
+
|
|
229
|
+
const T1_ROOT = resolve(
|
|
230
|
+
__dirname,
|
|
231
|
+
"../../../packages/aeo/__tests__/fixtures/t1-site",
|
|
232
|
+
);
|
|
233
|
+
const T1_CONTENT_ROOT = join(T1_ROOT, "content");
|
|
234
|
+
|
|
235
|
+
const READINESS_RULE_IDS = [
|
|
236
|
+
"policies-present",
|
|
237
|
+
"offer-on-priced-page",
|
|
238
|
+
"tax-code-when-stripe-tax",
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
describe("audit — readiness rules (#5949)", () => {
|
|
242
|
+
it("AC-1: the T1 fixture site passes all three through the MCP tool", async () => {
|
|
243
|
+
const result = await ailkAudit(
|
|
244
|
+
{ slug: "widget", type: "Product" },
|
|
245
|
+
T1_CONTENT_ROOT,
|
|
246
|
+
T1_ROOT,
|
|
247
|
+
);
|
|
248
|
+
for (const id of READINESS_RULE_IDS) {
|
|
249
|
+
const row = result.perRule.find((r) => r.ruleId === id);
|
|
250
|
+
expect(row).toBeDefined();
|
|
251
|
+
expect(row!.status).toBe("pass");
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("the tool's rule set is 9 rows and still validates against auditOutputSchema", async () => {
|
|
256
|
+
const result = await ailkAudit(
|
|
257
|
+
{ slug: "widget", type: "Product" },
|
|
258
|
+
T1_CONTENT_ROOT,
|
|
259
|
+
T1_ROOT,
|
|
260
|
+
);
|
|
261
|
+
expect(result.perRule).toHaveLength(9);
|
|
262
|
+
expect(auditOutputSchema.safeParse(result).success).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("D5: the site scan is the five READINESS_SCAN_TYPES and no more", async () => {
|
|
266
|
+
const input = await buildSinglePageAuditInput(
|
|
267
|
+
{ slug: "widget", type: "Product" },
|
|
268
|
+
T1_CONTENT_ROOT,
|
|
269
|
+
T1_ROOT,
|
|
270
|
+
);
|
|
271
|
+
const scanned = new Set(input.readiness!.sitePages.map((p) => p.type));
|
|
272
|
+
expect([...scanned].sort()).toEqual([...READINESS_SCAN_TYPES].sort());
|
|
273
|
+
// The fixture publishes a HomePage; it is deliberately outside the universe.
|
|
274
|
+
expect(scanned.has("HomePage")).toBe(false);
|
|
275
|
+
expect(input.readiness!.sitePages.find((p) => p.slug === "widget")).toEqual(
|
|
276
|
+
{
|
|
277
|
+
slug: "widget",
|
|
278
|
+
type: "Product",
|
|
279
|
+
price: 49,
|
|
280
|
+
taxCode: "txcd_99999999",
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("D5 fail-open-to-pass: a site the scan finds nothing in yields sitePages: [] and three passes", async () => {
|
|
286
|
+
const dir = mkdtempSync(join(tmpdir(), "ailk-mcp-readiness-"));
|
|
287
|
+
try {
|
|
288
|
+
mkdirSync(join(dir, "content", "en", "HomePage"), { recursive: true });
|
|
289
|
+
writeFileSync(
|
|
290
|
+
join(dir, "content", "en", "HomePage", "index.mdx"),
|
|
291
|
+
[
|
|
292
|
+
"---",
|
|
293
|
+
'type: "HomePage"',
|
|
294
|
+
'slug: "index"',
|
|
295
|
+
'title: "Nothing for sale"',
|
|
296
|
+
'description: "A site the readiness scan finds no priced page in."',
|
|
297
|
+
"---",
|
|
298
|
+
"",
|
|
299
|
+
"Body.",
|
|
300
|
+
].join("\n"),
|
|
301
|
+
"utf8",
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const input = await buildSinglePageAuditInput(
|
|
305
|
+
{ slug: "index", type: "HomePage" },
|
|
306
|
+
join(dir, "content"),
|
|
307
|
+
dir,
|
|
308
|
+
);
|
|
309
|
+
expect(input.readiness!.sitePages).toEqual([]);
|
|
310
|
+
|
|
311
|
+
const result = await ailkAudit(
|
|
312
|
+
{ slug: "index", type: "HomePage" },
|
|
313
|
+
join(dir, "content"),
|
|
314
|
+
dir,
|
|
315
|
+
);
|
|
316
|
+
for (const id of READINESS_RULE_IDS) {
|
|
317
|
+
expect(result.perRule.find((r) => r.ruleId === id)!.status).toBe(
|
|
318
|
+
"pass",
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
} finally {
|
|
322
|
+
rmSync(dir, { recursive: true, force: true });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("the projectRoot seam defaults to cwd when omitted (existing call sites unchanged)", async () => {
|
|
327
|
+
const result = await ailkAudit(
|
|
328
|
+
{ slug: "home", type: "HomePage" },
|
|
329
|
+
CONTENT_ROOT,
|
|
330
|
+
);
|
|
331
|
+
expect(result.perRule).toHaveLength(9);
|
|
332
|
+
expect(auditOutputSchema.safeParse(result).success).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe("readStripeTaxSignal — wiring smoke test (#6012)", () => {
|
|
337
|
+
// Not a duplicate of packages/aeo's own loadCommerceConfig suite (which
|
|
338
|
+
// covers the reader's parse/extract arms in isolation, against synthetic
|
|
339
|
+
// tmpdirs): this proves the EXPORTED apps/mcp wrapper itself is wired
|
|
340
|
+
// correctly — the directory probe delegating to loadCommerceConfig — against
|
|
341
|
+
// a real fixture file, since #6012 deleted the rest of this describe block
|
|
342
|
+
// as duplicate coverage but nothing else here exercises readStripeTaxSignal.
|
|
343
|
+
it("reads the T1 fixture's own project.yaml — Stripe Tax on", () => {
|
|
344
|
+
expect(readStripeTaxSignal(T1_ROOT)).toBe(true);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* What this asserts:
|
|
10
10
|
* - Tool names registered on the McpServer == catalog tool names
|
|
11
11
|
* - Tool descriptions registered on the McpServer == catalog descriptions
|
|
12
|
+
* - …UNDER BOTH commerce-claim states (#5971). The claim is what makes
|
|
13
|
+
* `start_purchase` visible, so comparing a claimed server against an
|
|
14
|
+
* unclaimed catalog (or vice versa) would compare unlike surfaces and
|
|
15
|
+
* report drift that isn't there — or, worse, miss drift that is.
|
|
12
16
|
*
|
|
13
17
|
* What this does NOT assert:
|
|
14
18
|
* - inputSchema equality — the server registers Zod schemas; the catalog
|
|
@@ -16,8 +20,14 @@
|
|
|
16
20
|
* (packages/validation), but distinct shapes downstream by design.
|
|
17
21
|
*/
|
|
18
22
|
|
|
23
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
24
|
import { getMcpToolCatalog } from "@working-theory/metadata";
|
|
20
25
|
|
|
26
|
+
import {
|
|
27
|
+
__setMcpCommerceClaimForTest,
|
|
28
|
+
resetMcpCommerceClaim,
|
|
29
|
+
type McpCommerceClaim,
|
|
30
|
+
} from "../src/lib/commerce-claim.js";
|
|
21
31
|
import { createMcpServer } from "../src/server.js";
|
|
22
32
|
|
|
23
33
|
interface RegisteredTool {
|
|
@@ -31,43 +41,76 @@ interface RegisteredTool {
|
|
|
31
41
|
// registers.
|
|
32
42
|
const PAID_MODULES = "scheduling,content-write,signal-collectors";
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The SDK exposes no public API for listing a server's tools without a live
|
|
46
|
+
* transport, so this reads the `_registeredTools` field directly. Pinned to
|
|
47
|
+
* @modelcontextprotocol/sdk ^1.29.0 — if the SDK renames the field, change it
|
|
48
|
+
* here once rather than at each call site.
|
|
49
|
+
*/
|
|
50
|
+
function registeredToolsOf(
|
|
51
|
+
server: McpServer,
|
|
52
|
+
): Record<string, RegisteredTool> {
|
|
53
|
+
return (
|
|
54
|
+
// eslint-disable-next-line no-restricted-syntax -- #506/#507: the SDK exposes no public accessor; the double assertion is confined to this one helper
|
|
55
|
+
server as unknown as { _registeredTools: Record<string, RegisteredTool> }
|
|
56
|
+
)._registeredTools;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The catalog options a given claim state implies — the same mapping
|
|
60
|
+
* `server.ts` applies internally, so both surfaces are read like for like. */
|
|
61
|
+
function catalogOptionsFor(claim: McpCommerceClaim) {
|
|
62
|
+
return { addons: { commerce: claim === "claimed" } };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe.each<McpCommerceClaim>(["unclaimed", "claimed"])(
|
|
66
|
+
"MCP catalog ↔ server drift — commerce claim %s",
|
|
67
|
+
(claim) => {
|
|
68
|
+
const priorEnabledModules = process.env.AILK_ENABLED_MODULES;
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
process.env.AILK_ENABLED_MODULES = PAID_MODULES;
|
|
72
|
+
__setMcpCommerceClaimForTest(claim);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
resetMcpCommerceClaim();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterAll(() => {
|
|
80
|
+
if (priorEnabledModules === undefined) {
|
|
81
|
+
delete process.env.AILK_ENABLED_MODULES;
|
|
82
|
+
} else {
|
|
83
|
+
process.env.AILK_ENABLED_MODULES = priorEnabledModules;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
36
86
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
87
|
+
it("registered tool names equal catalog tool names", () => {
|
|
88
|
+
const registered = registeredToolsOf(createMcpServer());
|
|
89
|
+
const registeredNames = Object.keys(registered).sort();
|
|
90
|
+
const catalogNames = getMcpToolCatalog(catalogOptionsFor(claim))
|
|
91
|
+
.map((t) => t.name)
|
|
92
|
+
.sort();
|
|
93
|
+
expect(registeredNames).toEqual(catalogNames);
|
|
94
|
+
});
|
|
40
95
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
96
|
+
it("registered tool descriptions equal catalog descriptions", () => {
|
|
97
|
+
const registered = registeredToolsOf(createMcpServer());
|
|
98
|
+
for (const entry of getMcpToolCatalog(catalogOptionsFor(claim))) {
|
|
99
|
+
expect(registered[entry.name]).toBeDefined();
|
|
100
|
+
expect(registered[entry.name]!.description).toBe(entry.description);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
48
103
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// eslint-disable-next-line no-restricted-syntax -- #506/#507: pre-existing test-hygiene violation
|
|
53
|
-
server as unknown as { _registeredTools: Record<string, RegisteredTool> }
|
|
54
|
-
)._registeredTools;
|
|
55
|
-
const registeredNames = Object.keys(registered).sort();
|
|
56
|
-
const catalogNames = getMcpToolCatalog()
|
|
57
|
-
.map((t) => t.name)
|
|
58
|
-
.sort();
|
|
59
|
-
expect(registeredNames).toEqual(catalogNames);
|
|
60
|
-
});
|
|
104
|
+
it("start_purchase appears in both surfaces exactly when the claim is held", () => {
|
|
105
|
+
const registered = registeredToolsOf(createMcpServer());
|
|
106
|
+
const expected = claim === "claimed";
|
|
61
107
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
108
|
+
expect("start_purchase" in registered).toBe(expected);
|
|
109
|
+
expect(
|
|
110
|
+
getMcpToolCatalog(catalogOptionsFor(claim)).some(
|
|
111
|
+
(t) => t.name === "start_purchase",
|
|
112
|
+
),
|
|
113
|
+
).toBe(expected);
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
);
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
* - Output validates against getOfferingsOutputSchema
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
rmSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { join, resolve } from "node:path";
|
|
14
21
|
|
|
15
22
|
import {
|
|
16
23
|
ailkGetOfferings,
|
|
@@ -88,3 +95,105 @@ describe("architectural guard — no console calls", () => {
|
|
|
88
95
|
expect(src).not.toMatch(/\bconsole\.(log|warn|error|info|debug)\b/);
|
|
89
96
|
});
|
|
90
97
|
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The offering fields on the catalog surface (#5838, spec 5465 D13/FR14).
|
|
101
|
+
*
|
|
102
|
+
* `get_offerings` is the one place an agent reads every Product and Service
|
|
103
|
+
* listing, so it returns the listing card's own fields — the provider, the
|
|
104
|
+
* categories, and the price when there is one — rather than leaving a caller to
|
|
105
|
+
* parse body copy for them. A listing with no price returns `null` for both
|
|
106
|
+
* money fields; it never fabricates a zero.
|
|
107
|
+
*/
|
|
108
|
+
describe("ailkGetOfferings — offering fields (#5838)", () => {
|
|
109
|
+
const mkdtemp = (): string =>
|
|
110
|
+
mkdtempSync(join(tmpdir(), "ailk-offerings-"));
|
|
111
|
+
|
|
112
|
+
function writeFixture(root: string): void {
|
|
113
|
+
mkdirSync(join(root, "en", "Product"), { recursive: true });
|
|
114
|
+
mkdirSync(join(root, "en", "Service"), { recursive: true });
|
|
115
|
+
writeFileSync(
|
|
116
|
+
join(root, "en", "Product", "standard.mdx"),
|
|
117
|
+
[
|
|
118
|
+
"---",
|
|
119
|
+
"type: Product",
|
|
120
|
+
"slug: standard",
|
|
121
|
+
"title: Standard",
|
|
122
|
+
"description: The flagship marketing template.",
|
|
123
|
+
"summary: A marketing site with docs, scheduling and flows.",
|
|
124
|
+
"thumbnail: /templates/standard.png",
|
|
125
|
+
"template: marketing-site",
|
|
126
|
+
"provider:",
|
|
127
|
+
" name: SalesSmyth",
|
|
128
|
+
" logo: /logomark.svg",
|
|
129
|
+
"categories:",
|
|
130
|
+
" - technology",
|
|
131
|
+
" - legal",
|
|
132
|
+
"---",
|
|
133
|
+
"",
|
|
134
|
+
"Body copy.",
|
|
135
|
+
"",
|
|
136
|
+
].join("\n"),
|
|
137
|
+
);
|
|
138
|
+
writeFileSync(
|
|
139
|
+
join(root, "en", "Service", "launch.mdx"),
|
|
140
|
+
[
|
|
141
|
+
"---",
|
|
142
|
+
"type: Service",
|
|
143
|
+
"slug: launch",
|
|
144
|
+
"title: Launch Service",
|
|
145
|
+
"description: A productized two-week deploy.",
|
|
146
|
+
"provider:",
|
|
147
|
+
" name: SalesSmyth",
|
|
148
|
+
"price: 2495",
|
|
149
|
+
"priceCurrency: USD",
|
|
150
|
+
"categories:",
|
|
151
|
+
" - technology",
|
|
152
|
+
"---",
|
|
153
|
+
"",
|
|
154
|
+
"Body copy.",
|
|
155
|
+
"",
|
|
156
|
+
].join("\n"),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let root: string;
|
|
161
|
+
|
|
162
|
+
beforeAll(() => {
|
|
163
|
+
root = mkdtemp();
|
|
164
|
+
writeFixture(root);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
afterAll(() => {
|
|
168
|
+
rmSync(root, { recursive: true, force: true });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("returns provider, categories and a null price for a listing with no price", async () => {
|
|
172
|
+
const result = await ailkGetOfferings({ type: "product" }, root);
|
|
173
|
+
const standard = result.offerings.find((o) => o.name === "Standard");
|
|
174
|
+
expect(standard).toBeDefined();
|
|
175
|
+
expect(standard?.provider).toEqual({
|
|
176
|
+
name: "SalesSmyth",
|
|
177
|
+
logo: "/logomark.svg",
|
|
178
|
+
});
|
|
179
|
+
expect(standard?.categories).toEqual(["technology", "legal"]);
|
|
180
|
+
expect(standard?.price).toBeNull();
|
|
181
|
+
expect(standard?.priceCurrency).toBeNull();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("returns the price and currency for a listing that carries them", async () => {
|
|
185
|
+
const result = await ailkGetOfferings({ type: "service" }, root);
|
|
186
|
+
const launch = result.offerings.find((o) => o.name === "Launch Service");
|
|
187
|
+
expect(launch?.price).toBe(2495);
|
|
188
|
+
expect(launch?.priceCurrency).toBe("USD");
|
|
189
|
+
expect(launch?.provider).toEqual({ name: "SalesSmyth" });
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("returns a null provider and an empty categories array for a page carrying neither", async () => {
|
|
193
|
+
const result = await ailkGetOfferings({}, CONTENT_ROOT);
|
|
194
|
+
for (const offering of result.offerings) {
|
|
195
|
+
expect(offering.provider === null || typeof offering.provider === "object").toBe(true);
|
|
196
|
+
expect(Array.isArray(offering.categories)).toBe(true);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -9,8 +9,15 @@
|
|
|
9
9
|
* - Output validates against getPricingOutputSchema
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
mkdirSync,
|
|
14
|
+
mkdtempSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
rmSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import { join, resolve } from "node:path";
|
|
14
21
|
|
|
15
22
|
import {
|
|
16
23
|
ailkGetPricing,
|
|
@@ -78,3 +85,76 @@ describe("architectural guard — no console calls", () => {
|
|
|
78
85
|
expect(src).not.toMatch(/\bconsole\.(log|warn|error|info|debug)\b/);
|
|
79
86
|
});
|
|
80
87
|
});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The offering-field fallback (#5838, spec 5465 D13/FR14).
|
|
91
|
+
*
|
|
92
|
+
* A listing page carries its price in frontmatter rather than hand-rolling an
|
|
93
|
+
* `Offer` into `schema`, so `get_pricing` reads that shape when the page
|
|
94
|
+
* authors no offer of its own. An authored offer still wins — this is a
|
|
95
|
+
* fallback, not a replacement — and a listing with no price at all still
|
|
96
|
+
* answers `null`.
|
|
97
|
+
*/
|
|
98
|
+
describe("ailkGetPricing — offering-field fallback (#5838)", () => {
|
|
99
|
+
let root: string;
|
|
100
|
+
|
|
101
|
+
beforeAll(() => {
|
|
102
|
+
root = mkdtempSync(join(tmpdir(), "ailk-pricing-"));
|
|
103
|
+
mkdirSync(join(root, "en", "Product"), { recursive: true });
|
|
104
|
+
writeFileSync(
|
|
105
|
+
join(root, "en", "Product", "priced.mdx"),
|
|
106
|
+
[
|
|
107
|
+
"---",
|
|
108
|
+
"type: Product",
|
|
109
|
+
"slug: priced",
|
|
110
|
+
"title: Standard",
|
|
111
|
+
"description: The flagship marketing template.",
|
|
112
|
+
"summary: A marketing site with docs, scheduling and flows.",
|
|
113
|
+
"price: 249",
|
|
114
|
+
"priceCurrency: USD",
|
|
115
|
+
"---",
|
|
116
|
+
"",
|
|
117
|
+
"Body copy.",
|
|
118
|
+
"",
|
|
119
|
+
].join("\n"),
|
|
120
|
+
);
|
|
121
|
+
writeFileSync(
|
|
122
|
+
join(root, "en", "Product", "free.mdx"),
|
|
123
|
+
[
|
|
124
|
+
"---",
|
|
125
|
+
"type: Product",
|
|
126
|
+
"slug: free",
|
|
127
|
+
"title: Standard (Free)",
|
|
128
|
+
"description: The free marketing template.",
|
|
129
|
+
"summary: A marketing site, free forever.",
|
|
130
|
+
"---",
|
|
131
|
+
"",
|
|
132
|
+
"Body copy.",
|
|
133
|
+
"",
|
|
134
|
+
].join("\n"),
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
afterAll(() => {
|
|
139
|
+
rmSync(root, { recursive: true, force: true });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("returns one tier from the page's own price and priceCurrency", async () => {
|
|
143
|
+
const result = await ailkGetPricing({ slug: "priced" }, root);
|
|
144
|
+
expect(result).toEqual({
|
|
145
|
+
tiers: [
|
|
146
|
+
{
|
|
147
|
+
name: "Standard",
|
|
148
|
+
price: 249,
|
|
149
|
+
priceCurrency: "USD",
|
|
150
|
+
description: "A marketing site with docs, scheduling and flows.",
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("returns null for a listing that declares no price — never a fabricated zero", async () => {
|
|
157
|
+
const result = await ailkGetPricing({ slug: "free" }, root);
|
|
158
|
+
expect(result).toBeNull();
|
|
159
|
+
});
|
|
160
|
+
});
|