create-ailk 0.22.0 → 0.24.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 +27 -14
- package/component-catalog.json +149 -5
- package/component-catalog.md +7 -6
- package/dist/add-module.js +17 -10
- package/dist/add-route.d.ts +55 -1
- package/dist/add-route.js +301 -13
- package/dist/cli.js +27 -10
- package/dist/derive-architecture.d.ts +7 -5
- package/dist/derive-architecture.js +26 -32
- package/dist/lib/design-checks.js +4 -2
- package/dist/lib/env-catalog.js +0 -4
- package/dist/lib/npm-auth.d.ts +9 -0
- package/dist/lib/npm-auth.js +48 -0
- package/dist/lib/scaffold-render-sweep.d.ts +36 -2
- package/dist/lib/scaffold-render-sweep.js +50 -8
- package/dist/module-architecture.js +115 -10
- package/dist/paid-sections.d.ts +21 -2
- package/dist/paid-sections.js +69 -7
- package/dist/seed-flows.d.ts +4 -1
- package/dist/seed-flows.js +50 -4
- package/dist/spoke-checkout.d.ts +38 -35
- package/dist/spoke-checkout.js +66 -229
- package/dist/starter-grid.d.ts +0 -3
- package/dist/starter-grid.js +24 -14
- package/dist/surfaces.js +33 -12
- package/dist/sync-routes.d.ts +68 -5
- package/dist/sync-routes.js +455 -29
- package/dist/template-record.d.ts +1 -1
- package/dist/template-record.js +3 -2
- package/dist/update.js +10 -0
- package/package.json +3 -3
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +4 -49
- package/site-templates/_base/content/en/Pricing/pricing.mdx +3 -21
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +23 -20
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +28 -14
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +15 -39
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +38 -34
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/templates/.env.example +13 -7
- package/templates/.github/workflows/ci.yml +13 -0
- package/templates/CLAUDE.md +4 -2
- package/templates/README.md +1 -0
- package/templates/_gitignore +4 -0
- package/templates/apps/api/.env.example +34 -0
- package/templates/apps/api/CLAUDE.md +94 -19
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +2 -0
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +45 -0
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +185 -0
- package/templates/apps/api/src/config/index.ts +109 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +7 -1
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +116 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +16 -0
- package/templates/apps/api/src/lib/error-serializer.ts +74 -7
- package/templates/apps/api/src/lib/site.ts +4 -1
- package/templates/apps/api/src/lib/stripe.ts +77 -4
- package/templates/apps/api/src/lib/webhook-idempotency.ts +66 -2
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +218 -0
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +174 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +226 -4
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +127 -0
- package/templates/apps/api/src/openapi/spec.ts +343 -7
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +332 -0
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +1426 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +49 -0
- package/templates/apps/api/src/routes/checkout/session.ts +57 -25
- package/templates/apps/api/src/routes/checkout/sessions.ts +232 -82
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +5 -5
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +160 -0
- package/templates/apps/api/src/routes/consent/account.ts +4 -2
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +2 -2
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +731 -0
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +753 -0
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +762 -0
- package/templates/apps/api/src/routes/content/get-page.ts +34 -119
- package/templates/apps/api/src/routes/content/index.ts +16 -2
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -36
- package/templates/apps/api/src/routes/content/offerings.ts +184 -0
- package/templates/apps/api/src/routes/content/pricing.ts +176 -0
- package/templates/apps/api/src/routes/content/search.ts +16 -45
- package/templates/apps/api/src/routes/content/shown-offerings.ts +229 -0
- package/templates/apps/api/src/routes/leads/index.ts +377 -308
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +20 -9
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +17 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +1 -1
- package/templates/apps/api/src/routes/webhooks/README.md +66 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +1 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +814 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +80 -68
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +6 -5
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +8 -2
- package/templates/apps/api/src/routes/webhooks/index.ts +23 -0
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +384 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +93 -17
- package/templates/apps/api/src/server.ts +165 -51
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +121 -4
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +313 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +61 -2
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +234 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +374 -44
- package/templates/apps/api/src/services/__tests__/person.test.ts +260 -1
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +450 -0
- package/templates/apps/api/src/services/__tests__/project-listings.counts.test.ts +202 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +15 -18
- package/templates/apps/api/src/services/abandon-email.ts +28 -5
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +15 -12
- package/templates/apps/api/src/services/entitlements.ts +157 -1
- package/templates/apps/api/src/services/lead-routing.ts +113 -58
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +348 -0
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +268 -0
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +975 -0
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +416 -0
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +400 -0
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +685 -0
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +231 -0
- package/templates/apps/api/src/services/offerings/catalog.ts +318 -0
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +403 -0
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +192 -0
- package/templates/apps/api/src/services/offerings/connection.ts +1083 -0
- package/templates/apps/api/src/services/offerings/index.ts +614 -0
- package/templates/apps/api/src/services/offerings/source.ts +140 -0
- package/templates/apps/api/src/services/offerings/store.ts +150 -0
- package/templates/apps/api/src/services/person.ts +147 -9
- package/templates/apps/api/src/services/plan-offerings.ts +96 -30
- package/templates/apps/api/src/services/project-listings.ts +43 -31
- package/templates/apps/api/src/services/public-site-resolver.ts +19 -14
- package/templates/apps/mcp/CLAUDE.md +32 -18
- package/templates/apps/mcp/__tests__/app-entry.test.ts +2 -2
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +9 -4
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +358 -0
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +364 -0
- package/templates/apps/mcp/__tests__/http-transport.test.ts +95 -14
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +8 -3
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -25
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +22 -7
- package/templates/apps/mcp/__tests__/list_pages.test.ts +84 -44
- package/templates/apps/mcp/__tests__/locale.test.ts +41 -56
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +2 -2
- package/templates/apps/mcp/__tests__/rate-limit-write.test.ts +189 -0
- package/templates/apps/mcp/__tests__/read_content.test.ts +115 -78
- package/templates/apps/mcp/__tests__/request_callback.test.ts +63 -128
- package/templates/apps/mcp/__tests__/search_content.test.ts +76 -61
- package/templates/apps/mcp/__tests__/server.test.ts +13 -11
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +208 -12
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +82 -167
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +59 -130
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +11 -11
- package/templates/apps/mcp/package.json +1 -0
- package/templates/apps/mcp/src/abuse/index.ts +18 -11
- package/templates/apps/mcp/src/http-transport.ts +47 -9
- package/templates/apps/mcp/src/lib/content-adapter.ts +0 -26
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +5 -4
- package/templates/apps/mcp/src/lib/shown-offerings.ts +105 -0
- package/templates/apps/mcp/src/lib/site-config.ts +53 -0
- package/templates/apps/mcp/src/server.ts +5 -0
- package/templates/apps/mcp/src/tools/capture_lead.ts +30 -28
- package/templates/apps/mcp/src/tools/get_offerings.ts +47 -30
- package/templates/apps/mcp/src/tools/get_pricing.ts +40 -40
- package/templates/apps/mcp/src/tools/index.ts +22 -21
- package/templates/apps/mcp/src/tools/list_pages.ts +27 -33
- package/templates/apps/mcp/src/tools/purchase.ts +30 -2
- package/templates/apps/mcp/src/tools/read_content.ts +46 -40
- package/templates/apps/mcp/src/tools/request_callback.ts +21 -28
- package/templates/apps/mcp/src/tools/search_content.ts +20 -24
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +23 -26
- package/templates/apps/mcp/src/workspace-server.ts +15 -0
- package/templates/apps/web/.env.example +10 -31
- package/templates/apps/web/CLAUDE.md +324 -42
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +189 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +4 -2
- package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +8 -3
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +22 -144
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +9 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-hub.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +149 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +22 -24
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +140 -66
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +57 -22
- package/templates/apps/web/app/[locale]/careers/page.tsx +129 -5
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +119 -11
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +28 -5
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +20 -6
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +1 -1
- package/templates/apps/web/app/[locale]/contact/page.tsx +99 -92
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +237 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.count.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.deliverable.test.tsx +200 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.ending.test.tsx +197 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +430 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +21 -9
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +369 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +12 -73
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +10 -31
- package/templates/apps/web/app/[locale]/layout.tsx +55 -4
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +174 -104
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +95 -58
- package/templates/apps/web/app/[locale]/pricing/__tests__/__snapshots__/unconnected.test.tsx.snap +7 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +599 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +216 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +236 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +367 -6
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +4 -1
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +17 -0
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +62 -3
- package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +70 -18
- package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +30 -9
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +316 -0
- package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +810 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +27 -1
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +2 -1
- package/templates/apps/web/lib/__tests__/in-page-magnet.test.ts +265 -0
- package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +64 -0
- package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +293 -0
- package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +601 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +67 -8
- package/templates/apps/web/lib/auth-routes.ts +36 -2
- package/templates/apps/web/lib/checkout-config.ts +9 -20
- package/templates/apps/web/lib/deck-page.ts +1 -0
- package/templates/apps/web/lib/deliverable-grant.ts +18 -0
- package/templates/apps/web/lib/in-page-magnet.ts +416 -0
- package/templates/apps/web/lib/magnet-routes.ts +44 -0
- package/templates/apps/web/lib/modules.generated.ts +9 -0
- package/templates/apps/web/lib/newsletter.actions.ts +205 -0
- package/templates/apps/web/lib/offerings-pricing.ts +326 -0
- package/templates/apps/web/lib/posting-title.ts +17 -0
- package/templates/apps/web/lib/sign-up-agreements.ts +4 -2
- package/templates/apps/web/messages/en.json +49 -15
- package/templates/apps/web/middleware.ts +12 -2
- package/templates/apps/web/security-headers.config.ts +17 -0
- package/templates/content/en/BlogList/blog.mdx +4 -71
- package/templates/content/en/BookCall/book-a-call.mdx +1 -41
- package/templates/content/en/BookCall/onsite-estimate.mdx +1 -17
- package/templates/content/en/BookCall/onsite-service.mdx +1 -17
- package/templates/content/en/BookCall/start-project.mdx +1 -17
- package/templates/content/en/ContactPage/contact.mdx +6 -63
- package/templates/content/en/HomePage/home.mdx +3 -21
- package/templates/database/CHANGELOG.md +51 -0
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +161 -0
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +270 -0
- package/templates/database/__tests__/offerings-migration.test.ts +197 -0
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +86 -0
- package/templates/database/__tests__/offerings.db.test.ts +303 -0
- package/templates/database/__tests__/seed.test.ts +63 -1
- package/templates/database/__tests__/trial-magnet-migration.test.ts +186 -0
- package/templates/database/auth/schema.prisma +4 -0
- package/templates/database/config/schema.prisma +91 -0
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +146 -0
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +77 -0
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +45 -0
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +47 -0
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +16 -0
- package/templates/database/migrations/20261025120000_trial_magnet/migration.sql +28 -0
- package/templates/database/migrations/20261026120000_trial_magnet_proof_line/migration.sql +23 -0
- package/templates/database/package.json +1 -1
- package/templates/database/seed.ts +57 -2
- package/templates/tools/lint-architecture.ts +205 -1
- package/versions.json +35 -35
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -199
- package/templates/apps/api/src/routes/content/slug.ts +0 -9
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +0 -93
- package/templates/apps/web/app/[locale]/downloads/page.tsx +0 -37
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +0 -139
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +0 -93
- package/templates/apps/web/app/api/checkout/[offering]/route.ts +0 -48
- package/templates/apps/web/app/api/checkout/__tests__/route.test.ts +0 -120
- package/templates/apps/web/app/api/checkout/__tests__/success-path.test.ts +0 -86
- package/templates/apps/web/app/api/webhooks/stripe/__tests__/route.test.ts +0 -92
- package/templates/apps/web/app/api/webhooks/stripe/route.ts +0 -31
- package/templates/apps/web/lib/__tests__/checkout.test.ts +0 -243
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +0 -120
- package/templates/apps/web/lib/checkout.ts +0 -93
- package/templates/apps/web/lib/meeting-slugs.ts +0 -63
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
import {
|
|
46
46
|
getPageSections,
|
|
47
47
|
pageToJsonLd,
|
|
48
|
+
siteOrigin,
|
|
48
49
|
type JsonLdObject,
|
|
49
50
|
} from "@working-theory/page-renderer";
|
|
50
51
|
import {
|
|
@@ -110,6 +111,13 @@ if (process.env.AILK_CONTENT_ROOT === undefined) {
|
|
|
110
111
|
process.env.AILK_CONTENT_ROOT = CONTENT_ROOT;
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
// A page that checks its own locale (`lib/locale-guard`, the meeting page)
|
|
115
|
+
// reads the enabled list next.config.mjs bakes in; pin it to the locale this
|
|
116
|
+
// file renders, as a built app would carry it.
|
|
117
|
+
if (process.env.NEXT_PUBLIC_ENABLED_LOCALES === undefined) {
|
|
118
|
+
process.env.NEXT_PUBLIC_ENABLED_LOCALES = DEFAULT_LOCALE;
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
// Mirrors the locale-directory rule in tools/content-compile and the adapter.
|
|
114
122
|
const LOCALE_RE = /^[a-z]{2}(-[A-Z]{2})?$/;
|
|
115
123
|
|
|
@@ -194,11 +202,8 @@ function extractJsonLdBlocks(container: HTMLElement): JsonLdObject[] {
|
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
const SITE_IDENTITY = readSiteIdentity(CONTENT_ROOT);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
process.env.MARKETING_WEB_BASE_URL ??
|
|
200
|
-
"http://localhost:3000"
|
|
201
|
-
).replace(/\/+$/, "");
|
|
205
|
+
// The resolver's own origin chain (#8797 D5), so this check cannot drift from it.
|
|
206
|
+
const SITE_ORIGIN = siteOrigin(SITE_IDENTITY);
|
|
202
207
|
|
|
203
208
|
/**
|
|
204
209
|
* The sitewide identity trio every rendered page carries when the site's own
|
|
@@ -305,6 +310,16 @@ const CHECKOUT_PAGE_MARKER = "Standard spoke checkout page (#8748)";
|
|
|
305
310
|
// record), so its updated copy of this test must still exempt them.
|
|
306
311
|
const LEGACY_CHECKOUT_PAGE_MARKER = "GENERATED by create-ailk (#7838)";
|
|
307
312
|
const CHECKOUT_PAGE_NOINDEX_LINE = "robots: { index: false, follow: false }";
|
|
313
|
+
// #8840 D13 — the marker every standard magnet page carries in its header
|
|
314
|
+
// comment (`apps/web/app/[locale]/flows/[slug]/magnet-page.tsx`), and the
|
|
315
|
+
// renderer it must import. A magnet page renders no `renderPage` call this
|
|
316
|
+
// harness can read, and its landing's JSON-LD comes from the hub's record, so
|
|
317
|
+
// a page carrying both is exempt at ANY route: `create-ailk add-route magnet`
|
|
318
|
+
// writes a pair at a path the site picks. Keyed on the file, as #8513 is.
|
|
319
|
+
const MAGNET_PAGE_MARKER = "Standard magnet page (#8840)";
|
|
320
|
+
const MAGNET_RENDERER_IMPORT = "flows/[slug]/magnet-page";
|
|
321
|
+
const MAGNET_PAGE_REASON =
|
|
322
|
+
"standard magnet page (#8840) — rendered by flows/[slug]/magnet-page; its landing's JSON-LD comes from the hub's flow record";
|
|
308
323
|
|
|
309
324
|
const NO_STRUCTURED_DATA_ROUTES: ReadonlyArray<{
|
|
310
325
|
route: string;
|
|
@@ -325,12 +340,12 @@ const NO_STRUCTURED_DATA_ROUTES: ReadonlyArray<{
|
|
|
325
340
|
// harness. Not checked here.
|
|
326
341
|
{
|
|
327
342
|
route: "blog",
|
|
328
|
-
reason: "fumadocs route —
|
|
343
|
+
reason: "fumadocs route — its .source collection is not built in this harness",
|
|
329
344
|
subtree: true,
|
|
330
345
|
},
|
|
331
346
|
{
|
|
332
347
|
route: "docs",
|
|
333
|
-
reason: "fumadocs route —
|
|
348
|
+
reason: "fumadocs route — its .source collection is not built in this harness",
|
|
334
349
|
subtree: true,
|
|
335
350
|
},
|
|
336
351
|
// Hub routes that enumerate a detail type with no list page type of their
|
|
@@ -340,6 +355,10 @@ const NO_STRUCTURED_DATA_ROUTES: ReadonlyArray<{
|
|
|
340
355
|
route: "how-to",
|
|
341
356
|
reason: "hand-composed listing; no HowTo list type exists",
|
|
342
357
|
},
|
|
358
|
+
{
|
|
359
|
+
route: "careers/[slug]/[action]",
|
|
360
|
+
reason: "the Free apply form for a posting; noindex, no JSON-LD (#8849 D7)",
|
|
361
|
+
},
|
|
343
362
|
{
|
|
344
363
|
route: "software",
|
|
345
364
|
reason: "hand-composed listing; no SoftwareProduct list type exists",
|
|
@@ -368,11 +387,23 @@ const NO_STRUCTURED_DATA_ROUTES: ReadonlyArray<{
|
|
|
368
387
|
reason: "interactive flow engine, API-backed",
|
|
369
388
|
subtree: true,
|
|
370
389
|
},
|
|
371
|
-
|
|
390
|
+
// #8843 D7 — the meeting page `meet/[slug]` composes its own body from a
|
|
391
|
+
// `BookCall` seed (`getMdxSource` + `pageToJsonLd`), so it is checked like
|
|
392
|
+
// any route; the two indexes and the confirmation page are not pages.
|
|
393
|
+
{ route: "meet", reason: "booking index, API-backed (#8843 D8)" },
|
|
394
|
+
{ route: "meet/confirmed", reason: "booking confirmation, API-backed" },
|
|
395
|
+
{ route: "private", reason: "private booking index, API-backed and noindex (#8843 D8)" },
|
|
372
396
|
{
|
|
373
397
|
route: "unsubscribe",
|
|
374
398
|
reason: "app-shaped landing, API-backed; no content page behind it",
|
|
375
399
|
},
|
|
400
|
+
// #8848 D12 — `/downloads` and `/downloads/<slug>` are permanent redirects
|
|
401
|
+
// into `/reports`; a redirect renders no page.
|
|
402
|
+
{
|
|
403
|
+
route: "downloads",
|
|
404
|
+
reason: "permanent redirect into /reports (#8848 D12); renders no page",
|
|
405
|
+
subtree: true,
|
|
406
|
+
},
|
|
376
407
|
// #6084 — both purchase return pages read a Checkout Session (or read
|
|
377
408
|
// nothing, for cancel) and render via SectionRenderer directly; neither
|
|
378
409
|
// is a content-adapter page, and the paid render's own schema.org node
|
|
@@ -440,6 +471,9 @@ function walkRoutes(dir: string, out: RouteEntry[] = []): RouteEntry[] {
|
|
|
440
471
|
}
|
|
441
472
|
|
|
442
473
|
function exemption(route: string, src: string): string | undefined {
|
|
474
|
+
if (src.includes(MAGNET_PAGE_MARKER) && src.includes(MAGNET_RENDERER_IMPORT)) {
|
|
475
|
+
return MAGNET_PAGE_REASON;
|
|
476
|
+
}
|
|
443
477
|
const entry = NO_STRUCTURED_DATA_ROUTES.find(
|
|
444
478
|
(e) =>
|
|
445
479
|
route === e.route ||
|
|
@@ -678,6 +712,31 @@ describe("structured data — exemption(route, src) keys the generated checkout
|
|
|
678
712
|
});
|
|
679
713
|
});
|
|
680
714
|
|
|
715
|
+
// ── 2b. exemption(route, src) — the standard magnet pages (#8840 D13) ────
|
|
716
|
+
|
|
717
|
+
describe("structured data — exemption(route, src) keys a standard magnet page on the file (#8840)", () => {
|
|
718
|
+
// The import line is assembled, not written out, so this file's own text
|
|
719
|
+
// carries no import of a path a scaffold's copy of it may not ship.
|
|
720
|
+
const IMPORT_LINE = ["import { magnetLanding } from ", JSON.stringify(`../${MAGNET_RENDERER_IMPORT}`), ";"].join("");
|
|
721
|
+
const PAIR_SRC = `// Generated by create-ailk add-route magnet.\n// ${MAGNET_PAGE_MARKER} — the landing of the /diagnostic magnet pair.\n${IMPORT_LINE}\n`;
|
|
722
|
+
|
|
723
|
+
it("exempts a page carrying the marker and the renderer import, at any route", () => {
|
|
724
|
+
expect(exemption("diagnostic", PAIR_SRC)).toBe(MAGNET_PAGE_REASON);
|
|
725
|
+
expect(exemption("diagnostic/start", PAIR_SRC)).toBe(MAGNET_PAGE_REASON);
|
|
726
|
+
expect(exemption("careers/writer", PAIR_SRC)).toBe(MAGNET_PAGE_REASON);
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
it("checks a page carrying the marker but no import of the renderer like any other", () => {
|
|
730
|
+
const noImport = `// ${MAGNET_PAGE_MARKER}\nexport default function Page() { return null; }\n`;
|
|
731
|
+
expect(exemption("diagnostic", noImport)).toBeUndefined();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it("checks a page importing the renderer without the marker like any other", () => {
|
|
735
|
+
const noMarker = `${IMPORT_LINE}\n`;
|
|
736
|
+
expect(exemption("diagnostic", noMarker)).toBeUndefined();
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
|
|
681
740
|
// ── 3. The two defect shapes that shipped, pinned ───────────────────────────
|
|
682
741
|
//
|
|
683
742
|
// These prove the checks above reach the validator rule that rejects each
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* signed-in account area — `/account` and everything under it — which the
|
|
13
13
|
* layout also renders without the marketing chrome.
|
|
14
14
|
*
|
|
15
|
+
* `isBookingPath()` (#8843 D10) is the same predicate for the scheduling
|
|
16
|
+
* module's booking pages — `/meet`, `/meet/<slug>` and `/private`, never
|
|
17
|
+
* `/meet/confirmed` — which the layout renders chrome-free only where the
|
|
18
|
+
* module is installed (`lib/modules.generated.ts`). This module imports
|
|
19
|
+
* nothing, so edge code can import it too.
|
|
20
|
+
*
|
|
15
21
|
* No Next.js import — pure functions, asserted directly by their own suite.
|
|
16
22
|
*/
|
|
17
23
|
|
|
@@ -62,6 +68,21 @@ export function isAccountPath(
|
|
|
62
68
|
return firstSegment(pathname, locales) === "account";
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Whether `pathname` addresses a booking page: `/private`, `/meet`, or
|
|
73
|
+
* `/meet/<slug>` for any slug but `confirmed` (the booker's confirmation
|
|
74
|
+
* page, which keeps the marketing chrome). The same prefix rule and the same
|
|
75
|
+
* fail-safe direction as `isAuthPath()`.
|
|
76
|
+
*/
|
|
77
|
+
export function isBookingPath(
|
|
78
|
+
pathname: string | undefined | null,
|
|
79
|
+
locales: readonly string[],
|
|
80
|
+
): boolean {
|
|
81
|
+
const [first, second] = segments(pathname, locales);
|
|
82
|
+
if (first === "private") return true;
|
|
83
|
+
return first === "meet" && second !== "confirmed";
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
/**
|
|
66
87
|
* The first path segment after an enabled-locale prefix, or `undefined` for
|
|
67
88
|
* an absent or empty pathname.
|
|
@@ -70,7 +91,18 @@ function firstSegment(
|
|
|
70
91
|
pathname: string | undefined | null,
|
|
71
92
|
locales: readonly string[],
|
|
72
93
|
): string | undefined {
|
|
73
|
-
|
|
94
|
+
return segments(pathname, locales)[0];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The path's segments after an enabled-locale prefix (empty for an absent or
|
|
99
|
+
* empty pathname). A trailing slash adds no segment.
|
|
100
|
+
*/
|
|
101
|
+
function segments(
|
|
102
|
+
pathname: string | undefined | null,
|
|
103
|
+
locales: readonly string[],
|
|
104
|
+
): string[] {
|
|
105
|
+
if (!pathname) return [];
|
|
74
106
|
|
|
75
107
|
// Drop a query string / fragment — `x-pathname` carries the path, but a
|
|
76
108
|
// caller passing a full request path should get the same verdict.
|
|
@@ -80,5 +112,7 @@ function firstSegment(
|
|
|
80
112
|
? path.replace(new RegExp(`^/(${locales.join("|")})(?=/|$)`), "")
|
|
81
113
|
: path;
|
|
82
114
|
|
|
83
|
-
|
|
115
|
+
const parts = bare.split("/").slice(1);
|
|
116
|
+
if (parts.at(-1) === "") parts.pop();
|
|
117
|
+
return parts;
|
|
84
118
|
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
// checkout-config.ts — this spoke's checkout, as data (#7838, #8748).
|
|
1
|
+
// checkout-config.ts — this spoke's checkout stamp, as data (#7838, #8748).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Written once by create-ailk and yours from then on: `create-ailk update`
|
|
4
|
+
// never rewrites it. Keep it to data: no import, and no code. A spoke sells
|
|
5
|
+
// through its hub's Offerings buy route, /api/offerings/checkout/<lookup_key>
|
|
6
|
+
// (#9142); the thank-you page and, on a spoke that signs in through its hub,
|
|
7
|
+
// the billing action read `site` from here.
|
|
8
8
|
//
|
|
9
|
-
// site
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// account this site shares. Never "default". Change it only before
|
|
13
|
-
// any price exists: every lookup key starts with it.
|
|
14
|
-
// offerings slug -> Stripe lookup_key. The buy link is /api/checkout/<slug>,
|
|
15
|
-
// and any other slug answers 404. Name an offering's slug after the
|
|
16
|
-
// last segment of the Service or Product page that sells it: that
|
|
17
|
-
// is the key the hub's plan rule and a magnet's checkoutOffering
|
|
18
|
-
// use. scaffold-commerce replaces the placeholder below.
|
|
9
|
+
// site The paid-session stamp the thank-you page checks
|
|
10
|
+
// (`metadata.site`) before it prefills "Open your account". Never
|
|
11
|
+
// "default". The create-ailk writer stamps the line below.
|
|
19
12
|
const SITE = "example-site";
|
|
20
13
|
|
|
21
14
|
export const checkoutConfig = {
|
|
22
15
|
site: SITE,
|
|
23
|
-
offerings: {
|
|
24
|
-
// A placeholder until scaffold-commerce creates this site's prices.
|
|
25
|
-
example: { kind: "fixed", lookupKey: `${SITE}_example`, cancelPath: "/" },
|
|
26
|
-
},
|
|
27
16
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape a deliverable grant id must match before anything spends a hub
|
|
3
|
+
* call on it (#9125). One definition, read by the report page
|
|
4
|
+
* (`app/[locale]/reports/[slug]/page.tsx`, `resolveGrantId`) and its file
|
|
5
|
+
* route (`app/[locale]/reports/[slug]/file/route.ts`), so the page and the
|
|
6
|
+
* bytes can never disagree on which ids are worth probing.
|
|
7
|
+
*
|
|
8
|
+
* A grant id is minted as `dgrant_<uuidv7>` (database/inbox/schema.prisma).
|
|
9
|
+
* This is deliberately a charset-and-length check, not a `dgrant_` prefix
|
|
10
|
+
* match: the API stays the authority on which ids are real, and a future id
|
|
11
|
+
* scheme should not need an edit here. 128 is a generous ceiling over today's
|
|
12
|
+
* `dgrant_<uuid>`.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately base-tree, not slice-owned: a keyed scaffold's
|
|
15
|
+
* signal-collectors bundle installs the page and route that import it, so it
|
|
16
|
+
* must already be in every scaffold.
|
|
17
|
+
*/
|
|
18
|
+
export const GRANT_ID_SHAPE = /^[A-Za-z0-9_-]{1,128}$/;
|