create-ailk 0.1.0 → 0.2.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 +124 -3
- package/component-catalog.md +456 -0
- package/dist/cli.js +92 -16
- package/dist/component-catalog.d.ts +11 -0
- package/dist/component-catalog.js +39 -0
- package/dist/copy.d.ts +7 -0
- package/dist/copy.js +24 -0
- package/dist/derive-architecture.d.ts +13 -0
- package/dist/derive-architecture.js +238 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +19 -2
- package/dist/lib/extract-module-bundle.d.ts +16 -0
- package/dist/lib/extract-module-bundle.js +184 -0
- package/dist/lib/fetch-module.d.ts +55 -0
- package/dist/lib/fetch-module.js +125 -0
- package/dist/lib/module-license-gate.d.ts +48 -0
- package/dist/lib/module-license-gate.js +67 -0
- package/dist/lib/module-tier.d.ts +35 -0
- package/dist/lib/module-tier.js +37 -0
- package/dist/module-architecture.d.ts +175 -0
- package/dist/module-architecture.js +663 -0
- package/dist/parse-args.d.ts +47 -1
- package/dist/parse-args.js +143 -2
- package/dist/programmatic.d.ts +160 -3
- package/dist/programmatic.js +196 -7
- package/dist/surfaces.d.ts +211 -0
- package/dist/surfaces.js +532 -0
- package/dist/sync-routes.d.ts +92 -0
- package/dist/sync-routes.js +350 -0
- package/package.json +12 -5
- package/templates/.claude/rules/architecture.md +6 -2
- package/templates/.claude/skills/README.md +9 -9
- package/templates/.claude/skills/add-schema/SKILL.md +14 -7
- package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
- package/templates/.claude/skills/provision-config/SKILL.md +1 -1
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
- package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
- package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
- package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
- package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
- package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
- package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
- package/templates/.env.example +126 -2
- package/templates/CLAUDE.md +11 -5
- package/templates/README.md +1 -1
- package/templates/apps/api/.env.example +38 -0
- package/templates/apps/api/CLAUDE.md +78 -1
- package/templates/apps/api/LICENSE +201 -0
- package/templates/apps/api/api/index.ts +6 -141
- package/templates/apps/api/jest.config.cjs +16 -0
- package/templates/apps/api/package.json +22 -6
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
- package/templates/apps/api/src/__tests__/server.test.ts +46 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
- package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
- package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
- package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
- package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
- package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
- package/templates/apps/api/src/bin/seed-presets.ts +58 -0
- package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
- package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
- package/templates/apps/api/src/cli.ts +5 -11
- package/templates/apps/api/src/config/index.ts +35 -0
- package/templates/apps/api/src/config/modules.ts +43 -0
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
- package/templates/apps/api/src/lib/site.ts +16 -0
- package/templates/apps/api/src/lib/stripe.ts +22 -0
- package/templates/apps/api/src/lib/tenant-db.ts +225 -0
- package/templates/apps/api/src/lib/ws-token.ts +91 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
- package/templates/apps/api/src/middleware/auth.ts +43 -8
- package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
- package/templates/apps/api/src/middleware/tenant.ts +99 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
- package/templates/apps/api/src/openapi/spec.ts +312 -3
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
- package/templates/apps/api/src/routes/aeo/index.ts +107 -0
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
- package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
- package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
- package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
- package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
- package/templates/apps/api/src/routes/billing/index.ts +36 -0
- package/templates/apps/api/src/routes/billing/portal.ts +182 -0
- package/templates/apps/api/src/routes/billing/read.ts +75 -0
- package/templates/apps/api/src/routes/billing/usage.ts +153 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
- package/templates/apps/api/src/routes/content/create.ts +1 -0
- package/templates/apps/api/src/routes/content/delete.ts +1 -0
- package/templates/apps/api/src/routes/content/index.ts +42 -6
- package/templates/apps/api/src/routes/content/update.ts +1 -0
- package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
- package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
- package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
- package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
- package/templates/apps/api/src/routes/flows/README.md +147 -0
- package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
- package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
- package/templates/apps/api/src/routes/flows/index.ts +202 -0
- package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
- package/templates/apps/api/src/routes/leads/index.ts +43 -9
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
- package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
- package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
- package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
- package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
- package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
- package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
- package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
- package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
- package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
- package/templates/apps/api/src/routes/schedule/index.ts +249 -0
- package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
- package/templates/apps/api/src/routes/slack/actions.ts +177 -0
- package/templates/apps/api/src/routes/slack/index.ts +38 -0
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
- package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
- package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
- package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
- package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
- package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
- package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
- package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
- package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
- package/templates/apps/api/src/routes/webhooks/README.md +35 -12
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
- package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
- package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
- package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
- package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
- package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
- package/templates/apps/api/src/server.ts +307 -7
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
- package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
- package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
- package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
- package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
- package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
- package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
- package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
- package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
- package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
- package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
- package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
- package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
- package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
- package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
- package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
- package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
- package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
- package/templates/apps/api/src/services/abandon-email.ts +307 -0
- package/templates/apps/api/src/services/aeo-score.ts +288 -0
- package/templates/apps/api/src/services/answer-distributions.ts +138 -0
- package/templates/apps/api/src/services/consent.ts +236 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
- package/templates/apps/api/src/services/entitlements.ts +102 -0
- package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
- package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
- package/templates/apps/api/src/services/flow-engine.ts +1296 -0
- package/templates/apps/api/src/services/lead-promotion.ts +176 -0
- package/templates/apps/api/src/services/lead-routing.ts +3 -3
- package/templates/apps/api/src/services/listing-config.ts +102 -0
- package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
- package/templates/apps/api/src/services/listing-promotion.ts +342 -0
- package/templates/apps/api/src/services/playbook-compile.ts +398 -0
- package/templates/apps/api/src/services/playbook-render.ts +263 -0
- package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
- package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
- package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
- package/templates/apps/api/src/services/project-listings.ts +1472 -0
- package/templates/apps/api/src/services/recommender-capture.ts +835 -0
- package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
- package/templates/apps/api/src/services/scheduling/index.ts +63 -0
- package/templates/apps/api/src/services/scheduling/types.ts +88 -0
- package/templates/apps/api/src/services/slack-notify.ts +240 -0
- package/templates/apps/api/src/services/tenant-context.ts +451 -0
- package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
- package/templates/apps/api/src/services/usage-credits.ts +340 -0
- package/templates/apps/api/src/services/usage-metering.ts +699 -0
- package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
- package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
- package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
- package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
- package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
- package/templates/apps/api/src/vercel-handler.ts +165 -0
- package/templates/apps/api/tsconfig.json +0 -12
- package/templates/apps/mcp/CLAUDE.md +19 -9
- package/templates/apps/mcp/LICENSE +201 -0
- package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
- package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
- package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
- package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
- package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
- package/templates/apps/mcp/package.json +6 -4
- package/templates/apps/mcp/src/config/modules.ts +37 -0
- package/templates/apps/mcp/src/server.ts +109 -18
- package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
- package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
- package/templates/apps/mcp/src/tools/create_page.ts +1 -0
- package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
- package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
- package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
- package/templates/apps/mcp/src/tools/index.ts +25 -0
- package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
- package/templates/apps/mcp/src/tools/purchase.ts +3 -0
- package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
- package/templates/apps/mcp/src/tools/update_page.ts +1 -0
- package/templates/apps/mcp/tsconfig.json +1 -9
- package/templates/apps/web/.env.example +44 -0
- package/templates/apps/web/CLAUDE.md +28 -0
- package/templates/apps/web/LICENSE +201 -0
- package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
- package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
- package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
- package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
- package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
- package/templates/apps/web/__tests__/setup.ts +22 -32
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
- package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
- package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/layout.tsx +127 -16
- package/templates/apps/web/app/[locale]/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
- package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
- package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
- package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
- package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
- package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/globals.css +35 -0
- package/templates/apps/web/app/llms.txt/route.ts +2 -1
- package/templates/apps/web/app/og/route.tsx +10 -2
- package/templates/apps/web/auth.ts +8 -0
- package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
- package/templates/apps/web/content/en/blog/README.txt +25 -0
- package/templates/apps/web/content/en/docs/index.mdx +28 -0
- package/templates/apps/web/jest.config.cjs +90 -27
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
- package/templates/apps/web/lib/account-config.ts +13 -0
- package/templates/apps/web/lib/server-api.ts +85 -0
- package/templates/apps/web/lib/site-brand.tsx +121 -0
- package/templates/apps/web/lib/site-theme.ts +74 -0
- package/templates/apps/web/messages/en.json +275 -7
- package/templates/apps/web/middleware.ts +11 -3
- package/templates/apps/web/next.config.mjs +8 -2
- package/templates/apps/web/package.json +17 -7
- package/templates/apps/web/postcss.config.mjs +23 -0
- package/templates/apps/web/public/.well-known/security.txt +3 -3
- package/templates/apps/web/src/lib/routes.ts +15 -2
- package/templates/apps/web/tsconfig.json +1 -16
- package/templates/content/_site.mdx +36 -25
- package/templates/content/en/AboutPage/about.mdx +24 -14
- package/templates/content/en/ContactPage/contact.mdx +23 -14
- package/templates/content/en/FAQPage/faq.mdx +10 -392
- package/templates/content/en/HomePage/home.mdx +14 -285
- package/templates/database/CHANGELOG.md +443 -0
- package/templates/database/LICENSE +201 -0
- package/templates/database/README.md +3 -1
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
- package/templates/database/__tests__/flows-presets.test.ts +173 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
- package/templates/database/__tests__/seed.test.ts +88 -0
- package/templates/database/__tests__/user-cascade.test.ts +19 -4
- package/templates/database/auth/schema.prisma +521 -0
- package/templates/database/config/schema.prisma +234 -0
- package/templates/database/content/schema.prisma +263 -0
- package/templates/database/flows/listing-diagnostic.ts +182 -0
- package/templates/database/flows/presets.ts +315 -0
- package/templates/database/inbox/__tests__/schema.test.ts +4 -0
- package/templates/database/inbox/schema.prisma +574 -3
- package/templates/database/jest.config.cjs +8 -0
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
- package/templates/database/ops/schema.prisma +27 -0
- package/templates/database/package.json +42 -4
- package/templates/database/prisma.config.ts +1 -1
- package/templates/database/schema.prisma +19 -435
- package/templates/database/seed-test.ts +14 -4
- package/templates/database/seed.ts +148 -0
- package/templates/database/tenancy.ts +45 -0
- package/templates/database/tsconfig.flows.json +15 -0
- package/templates/database/tsconfig.json +7 -1
- package/templates/database/tsconfig.tenancy.json +14 -0
- package/templates/package.json +30 -55
- package/templates/pnpm-workspace.yaml +0 -3
- package/templates/project.yaml +1 -1
- package/templates/tsconfig.json +1 -37
- package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
- package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
- package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
- package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
- package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
- package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
- package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
- package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
- package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
- package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
- package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
- package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
- package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
- package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
- package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
- package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
- package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
- package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
- package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
- package/templates/apps/web/__tests__/page.test.tsx +0 -230
- package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
- package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
- package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
- package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
- package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
- package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
- package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
- package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
- package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
- package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
- package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
- package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
- package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
- package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
- package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
- package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
- package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
- package/templates/apps/web/content/en/blog/index.mdx +0 -15
- package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
- package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
- package/templates/content/.gitkeep +0 -0
- package/templates/content/CLAUDE.md +0 -85
- package/templates/content/ar/HomePage/home.mdx +0 -123
- package/templates/content/ar/_site.mdx +0 -35
- package/templates/content/de/_site.mdx +0 -29
- package/templates/content/en/.gitkeep +0 -0
- package/templates/content/en/AboutPage/about.schema.json +0 -82
- package/templates/content/en/BookCall/book-call.mdx +0 -19
- package/templates/content/en/BookCall/book-call.schema.json +0 -121
- package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
- package/templates/content/en/CareersList/careers.mdx +0 -24
- package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
- package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
- package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
- package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
- package/templates/content/en/ClientsList/clients.mdx +0 -38
- package/templates/content/en/ContactPage/contact.schema.json +0 -100
- package/templates/content/en/ContactPoint/primary.mdx +0 -14
- package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
- package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
- package/templates/content/en/CoursesList/courses.mdx +0 -22
- package/templates/content/en/DownloadsList/downloads.mdx +0 -22
- package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
- package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
- package/templates/content/en/EventsList/events.mdx +0 -26
- package/templates/content/en/FAQPage/faq.schema.json +0 -231
- package/templates/content/en/GlossaryList/glossary.mdx +0 -19
- package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
- package/templates/content/en/HomePage/home.schema.json +0 -88
- package/templates/content/en/HomePage/launch-service.mdx +0 -475
- package/templates/content/en/HomePage/launch-service.schema.json +0 -138
- package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
- package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
- package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
- package/templates/content/en/Integration/github-actions.mdx +0 -31
- package/templates/content/en/Integration/openai-api.mdx +0 -26
- package/templates/content/en/Integration/vercel.mdx +0 -32
- package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
- package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
- package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
- package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
- package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
- package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
- package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
- package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
- package/templates/content/en/LandingPage/developers.mdx +0 -19
- package/templates/content/en/LandingPage/developers.schema.json +0 -109
- package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
- package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
- package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
- package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
- package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
- package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
- package/templates/content/en/LandingPage/platform.mdx +0 -369
- package/templates/content/en/LandingPage/platform.schema.json +0 -166
- package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
- package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
- package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
- package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
- package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
- package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
- package/templates/content/en/Organization/site-owner.mdx +0 -16
- package/templates/content/en/Partner/prisma.mdx +0 -31
- package/templates/content/en/Partner/supabase.mdx +0 -30
- package/templates/content/en/Partner/vercel.mdx +0 -28
- package/templates/content/en/PartnersList/partners.mdx +0 -22
- package/templates/content/en/Person/founder.mdx +0 -18
- package/templates/content/en/Pricing/pricing.mdx +0 -538
- package/templates/content/en/Pricing/pricing.schema.json +0 -209
- package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
- package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
- package/templates/content/en/Product/ailk.mdx +0 -19
- package/templates/content/en/Product/ailk.schema.json +0 -94
- package/templates/content/en/Product/api-launch-kit.mdx +0 -26
- package/templates/content/en/Product/launch-kit.mdx +0 -28
- package/templates/content/en/ProductsList/products.mdx +0 -26
- package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
- package/templates/content/en/ProjectsList/projects.mdx +0 -25
- package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
- package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
- package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
- package/templates/content/en/ResourcesHub/resources.mdx +0 -22
- package/templates/content/en/Service/consulting.mdx +0 -22
- package/templates/content/en/Service/implementation.mdx +0 -30
- package/templates/content/en/Service/launch-service.mdx +0 -232
- package/templates/content/en/Service/launch.mdx +0 -228
- package/templates/content/en/ServicesList/services.mdx +0 -84
- package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
- package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
- package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
- package/templates/content/en/TeamList/team.mdx +0 -63
- package/templates/content/en/TeamMember/founder.mdx +0 -72
- package/templates/content/en/TermsPage/terms.mdx +0 -19
- package/templates/content/en/TermsPage/terms.schema.json +0 -99
- package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
- package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
- package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
- package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
- package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
- package/templates/content/en-XA/AboutPage/about.mdx +0 -8
- package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
- package/templates/content/en-XA/HomePage/home.mdx +0 -8
- package/templates/content/es/_site.mdx +0 -30
- package/templates/content/fr/_site.mdx +0 -30
- package/templates/content/ja/_site.mdx +0 -30
- package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
- package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
- package/templates/content/pt-BR/HomePage/home.mdx +0 -23
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// config namespace — site-wide settings and customer configuration (ADR-0009).
|
|
2
|
+
//
|
|
3
|
+
// SiteSetting is the site-wide key-value config store. Generator + datasource
|
|
4
|
+
// live in database/schema.prisma (the folder's single config file).
|
|
5
|
+
//
|
|
6
|
+
// The three site-config models (WorkspaceTheme / WorkspaceSiteIdentity /
|
|
7
|
+
// WorkspaceSEO) are workspace-scoped: keyed on a plain `workspaceId String` with
|
|
8
|
+
// NO cross-namespace @relation to auth/Workspace (a stable-ID per ADR-0009
|
|
9
|
+
// invariant #1). Cross-namespace integrity + cascade are the service layer's
|
|
10
|
+
// (ADR-0009 D3), delivered by the Phase 4 workspace-scoped write surface. Every
|
|
11
|
+
// model here is fenced by the architecture.yaml tenant-scope forbidden-pattern.
|
|
12
|
+
|
|
13
|
+
/// SiteSetting - Key-value settings for the customer site
|
|
14
|
+
/// Use for: feature flags, custom config, cached Platform data
|
|
15
|
+
model SiteSetting {
|
|
16
|
+
id String @id @default(dbgenerated("('siteset_'::text || (uuidv7())::text)")) @db.Text
|
|
17
|
+
key String @unique // "feature_flags", "cached_config", "custom_theme"
|
|
18
|
+
value Json
|
|
19
|
+
|
|
20
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
21
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
22
|
+
|
|
23
|
+
@@map("site_settings")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Workspace site config — workspace-scoped design-system persistence (#3641).
|
|
28
|
+
//
|
|
29
|
+
// Three tables, one per @working-theory/site-config schema. Each row stores the
|
|
30
|
+
// Zod-validated JSON blob for a single workspace. Validation is enforced at the
|
|
31
|
+
// API layer; the DB stores the already-validated blob in a Json column.
|
|
32
|
+
//
|
|
33
|
+
// Design decisions (site-config-reanchor-spec §3):
|
|
34
|
+
// - workspaceId is a plain String stable-ID — NO @relation to auth/Workspace
|
|
35
|
+
// (ADR-0009 invariant #1: no cross-namespace @relation).
|
|
36
|
+
// - One table per schema (not a polymorphic config table) — different update
|
|
37
|
+
// cadences + consumer access patterns.
|
|
38
|
+
// - Json column for the blob — schema evolves without a DB migration on change.
|
|
39
|
+
// - @unique(workspaceId) — one active record per workspace per config type.
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
/// WorkspaceTheme — per-workspace ThemeSchema blob (#3641)
|
|
43
|
+
model WorkspaceTheme {
|
|
44
|
+
id String @id @default(dbgenerated("('theme_'::text || (uuidv7())::text)")) @db.Text
|
|
45
|
+
workspaceId String @unique @map("workspace_id")
|
|
46
|
+
data Json
|
|
47
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
48
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
49
|
+
|
|
50
|
+
@@map("workspace_themes")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// WorkspaceSiteIdentity — per-workspace SiteIdentitySchema blob (#3641)
|
|
54
|
+
model WorkspaceSiteIdentity {
|
|
55
|
+
id String @id @default(dbgenerated("('siteid_'::text || (uuidv7())::text)")) @db.Text
|
|
56
|
+
workspaceId String @unique @map("workspace_id")
|
|
57
|
+
data Json
|
|
58
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
59
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
60
|
+
|
|
61
|
+
@@map("workspace_site_identities")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// WorkspaceSEO — per-workspace SEOSchema blob (#3641)
|
|
65
|
+
model WorkspaceSEO {
|
|
66
|
+
id String @id @default(dbgenerated("('seo_'::text || (uuidv7())::text)")) @db.Text
|
|
67
|
+
workspaceId String @unique @map("workspace_id")
|
|
68
|
+
data Json
|
|
69
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
70
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
71
|
+
|
|
72
|
+
@@map("workspace_seos")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// SiteListingConfig — per-SITE project-listings config blob (#5033).
|
|
76
|
+
///
|
|
77
|
+
/// The fourth site-config model, and the one keyed on `siteId` rather than
|
|
78
|
+
/// `workspaceId`. `ProjectListing` is scoped by `siteId`, and its create path
|
|
79
|
+
/// is anonymous (a public application form, no session) — at the moment a
|
|
80
|
+
/// listing is created there is no workspace to resolve, so a tenant-guarded
|
|
81
|
+
/// workspace config would be unreadable from the one place these values are
|
|
82
|
+
/// needed. Otherwise identical to its three siblings above: `@unique` key,
|
|
83
|
+
/// one Zod-validated JSON blob (`siteListingConfigSchema` in
|
|
84
|
+
/// @working-theory/validation), validation at the API layer.
|
|
85
|
+
///
|
|
86
|
+
/// Carries the listing-window bounds and the featured switch — a site's own
|
|
87
|
+
/// commercial terms, which is why they are here and not a constant in the
|
|
88
|
+
/// published package. Never written from a request body; read server-side by
|
|
89
|
+
/// the anonymous create handler.
|
|
90
|
+
model SiteListingConfig {
|
|
91
|
+
id String @id @default(dbgenerated("('sltcfg_'::text || (uuidv7())::text)")) @db.Text
|
|
92
|
+
siteId String @unique @map("site_id")
|
|
93
|
+
|
|
94
|
+
/// The site's PUBLIC site key (#5048 D2) — the addressing token an anonymous
|
|
95
|
+
/// application carries in place of a siteId, so the server resolves the site
|
|
96
|
+
/// rather than trusting a request body. Public by design: it ships in browser
|
|
97
|
+
/// JavaScript and in an agent's request, and grants nothing on its own.
|
|
98
|
+
///
|
|
99
|
+
/// Nullable and @unique: a site that has not minted one is unreachable by key
|
|
100
|
+
/// and falls back to the single-tenant env path (AILK_LISTING_PUBLIC_KEY, see
|
|
101
|
+
/// apps/api/src/services/listing-config.ts), which is how the default
|
|
102
|
+
/// create-ailk scaffold works with no config row at all.
|
|
103
|
+
publicKey String? @unique @map("public_key")
|
|
104
|
+
|
|
105
|
+
data Json
|
|
106
|
+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
107
|
+
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
108
|
+
|
|
109
|
+
@@map("site_listing_configs")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================================================
|
|
113
|
+
// Usage-billing pricing/markup/model config (FEAT-047 P1.S2, #3710 — Epic
|
|
114
|
+
// #3704). Per ADR 0009 D2 (roadmap §4), pricing/markup/model config lives in
|
|
115
|
+
// `config`; balances/subscription/plan live in `auth` (see
|
|
116
|
+
// database/auth/schema.prisma). organizationId is a plain String stable-id —
|
|
117
|
+
// no @relation (ADR 0009 invariant 1). LlmModel.tier references ModelTier,
|
|
118
|
+
// defined in database/content/schema.prisma (FEAT-047 P1.S1, #3709, spec
|
|
119
|
+
// OQ-1) — an enum reference through the merged prismaSchemaFolder needs no
|
|
120
|
+
// @relation, so this is not a cross-namespace FK; ModelTier is NOT
|
|
121
|
+
// redefined here. Field roster ported from
|
|
122
|
+
// docs/research/wt-billing-capture/schema.md.
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
/// BillingMode — org- vs individual-billed. Ported from the capture (schema.md
|
|
126
|
+
/// § balance counters) for forward compatibility; no P1.S2 field consumes it
|
|
127
|
+
/// yet (Organization's billing-mode projection is out of this spec's field
|
|
128
|
+
/// roster — spec §2.1 item 1). Captured now so the enum name is stable when a
|
|
129
|
+
/// later phase wires it.
|
|
130
|
+
enum BillingMode {
|
|
131
|
+
INDIVIDUAL
|
|
132
|
+
ORGANIZATION
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// SubscriptionPaymentMode — how a Subscription is paid. Self-serve
|
|
136
|
+
/// subscriptions default to CARD; Subscription.paymentMode is nullable
|
|
137
|
+
/// (enterprise-only field, spec §5.3 — no business default invented).
|
|
138
|
+
enum SubscriptionPaymentMode {
|
|
139
|
+
CARD
|
|
140
|
+
INVOICE
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// BillingCycle — MONTHLY vs YEARLY. Ported from the capture for forward
|
|
144
|
+
/// compatibility; no P1.S2 field consumes it yet (Plan's Stripe price id is
|
|
145
|
+
/// singular in this spec's field roster — a monthly/yearly price-id split is
|
|
146
|
+
/// a future additive column, not invented here).
|
|
147
|
+
enum BillingCycle {
|
|
148
|
+
MONTHLY
|
|
149
|
+
YEARLY
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// ContractType — how a Subscription's deal was made. Subscription.contractType
|
|
153
|
+
/// is nullable (enterprise-only field — self-serve subscriptions leave it
|
|
154
|
+
/// unset).
|
|
155
|
+
enum ContractType {
|
|
156
|
+
SELF_SERVE
|
|
157
|
+
SALES_ASSISTED
|
|
158
|
+
ENTERPRISE
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// AiMarkupConfig — hierarchical per-org markup config: null provider+model =
|
|
162
|
+
/// org default; null model = provider-wide; both set = model-specific
|
|
163
|
+
/// (spec §2.1 item 5).
|
|
164
|
+
model AiMarkupConfig {
|
|
165
|
+
id String @id @default(dbgenerated("('markup_'::text || (uuidv7())::text)")) @db.Text
|
|
166
|
+
organizationId String @map("organization_id")
|
|
167
|
+
provider String?
|
|
168
|
+
model String?
|
|
169
|
+
|
|
170
|
+
markupPercent Decimal @map("markup_percent") @db.Decimal(5, 2)
|
|
171
|
+
|
|
172
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
173
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
174
|
+
|
|
175
|
+
@@unique([organizationId, provider, model])
|
|
176
|
+
@@map("ai_markup_configs")
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// LlmModelPricing — per-token provider rates, time-versioned by
|
|
180
|
+
/// effectiveFrom/effectiveTo (spec §2.1 item 6).
|
|
181
|
+
model LlmModelPricing {
|
|
182
|
+
id String @id @default(dbgenerated("('llmprc_'::text || (uuidv7())::text)")) @db.Text
|
|
183
|
+
provider String
|
|
184
|
+
model String
|
|
185
|
+
|
|
186
|
+
inputPricePerMillion Decimal @map("input_price_per_million") @db.Decimal(10, 4)
|
|
187
|
+
outputPricePerMillion Decimal @map("output_price_per_million") @db.Decimal(10, 4)
|
|
188
|
+
cachedInputPricePerMillion Decimal? @map("cached_input_price_per_million") @db.Decimal(10, 4)
|
|
189
|
+
|
|
190
|
+
effectiveFrom DateTime @map("effective_from")
|
|
191
|
+
effectiveTo DateTime? @map("effective_to")
|
|
192
|
+
|
|
193
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
194
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
195
|
+
|
|
196
|
+
@@unique([provider, model, effectiveFrom])
|
|
197
|
+
@@map("llm_model_pricing")
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// LlmModel — the active model catalog: tier, consumption weighting, context
|
|
201
|
+
/// window (spec §2.1 item 7). provider+model is the natural key (not
|
|
202
|
+
/// time-versioned, unlike LlmModelPricing — this is the current-state
|
|
203
|
+
/// catalog row per model).
|
|
204
|
+
model LlmModel {
|
|
205
|
+
id String @id @default(dbgenerated("('llmmdl_'::text || (uuidv7())::text)")) @db.Text
|
|
206
|
+
provider String
|
|
207
|
+
model String
|
|
208
|
+
|
|
209
|
+
tier ModelTier
|
|
210
|
+
consumptionMultiplier Decimal @default(1.0) @map("consumption_multiplier") @db.Decimal(5, 2)
|
|
211
|
+
chargeExtendedContextAt2x Boolean @map("charge_extended_context_at_2x")
|
|
212
|
+
isAutoModel Boolean @map("is_auto_model")
|
|
213
|
+
defaultContext Int @map("default_context")
|
|
214
|
+
maxContext Int @map("max_context")
|
|
215
|
+
|
|
216
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
217
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
218
|
+
|
|
219
|
+
@@unique([provider, model])
|
|
220
|
+
@@map("llm_models")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/// HobbyResetTracker — drives the 90-day Hobby-tier reset (P4, spec §2.1
|
|
224
|
+
/// item 8).
|
|
225
|
+
model HobbyResetTracker {
|
|
226
|
+
id String @id @default(dbgenerated("('hbyrst_'::text || (uuidv7())::text)")) @db.Text
|
|
227
|
+
organizationId String @unique @map("organization_id")
|
|
228
|
+
lastResetAt DateTime @map("last_reset_at")
|
|
229
|
+
nextResetAt DateTime @map("next_reset_at")
|
|
230
|
+
|
|
231
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
232
|
+
|
|
233
|
+
@@map("hobby_reset_trackers")
|
|
234
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// content namespace — chat + artifact substrate (AgentSession, ConversationEvent,
|
|
2
|
+
// SessionReadReceipt, Artifact, ArtifactVersion)
|
|
3
|
+
//
|
|
4
|
+
// Backs the event-sourced conversation transport + artifact persistence in
|
|
5
|
+
// docs/research/ploy-capture/BACKEND-REQUIREMENTS.md (§1 transport R-T1..R-T7,
|
|
6
|
+
// §2 event envelope, §3 endpoints). Workspaces own all domain content, so every
|
|
7
|
+
// table here carries a non-null `workspaceId` tenant scope.
|
|
8
|
+
//
|
|
9
|
+
// Per ADR 0009 invariant 1: workspaceId / userId are plain String stable IDs —
|
|
10
|
+
// NO @relation into the auth namespace (Workspace/User live in database/auth/).
|
|
11
|
+
// Relations WITHIN this namespace (event → session, version → artifact) are
|
|
12
|
+
// real FKs. Cross-namespace assembly (e.g. tenant guard resolving a session's
|
|
13
|
+
// workspace) routes through apps/api/src/services/ (invariant 4).
|
|
14
|
+
//
|
|
15
|
+
// Generator + datasource blocks live in database/schema.prisma (the root schema
|
|
16
|
+
// file). This file holds only model definitions for the content namespace.
|
|
17
|
+
|
|
18
|
+
/// AgentSession — one agent conversation ("session"/captured "ploy") within a
|
|
19
|
+
/// workspace. lastSeq is the append watermark for the event log (R-T1).
|
|
20
|
+
model AgentSession {
|
|
21
|
+
id String @id @default(dbgenerated("('agsess_'::text || (uuidv7())::text)")) @db.Text
|
|
22
|
+
workspaceId String @map("workspace_id")
|
|
23
|
+
title String?
|
|
24
|
+
status String @default("idle")
|
|
25
|
+
lastSeq Int @default(0) @map("last_seq")
|
|
26
|
+
lastMessageAt DateTime? @map("last_message_at")
|
|
27
|
+
draftMessage String? @map("draft_message")
|
|
28
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
29
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
30
|
+
|
|
31
|
+
events ConversationEvent[]
|
|
32
|
+
artifacts Artifact[]
|
|
33
|
+
readReceipts SessionReadReceipt[]
|
|
34
|
+
|
|
35
|
+
@@index([workspaceId, lastMessageAt(sort: Desc)])
|
|
36
|
+
@@map("agent_sessions")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// ConversationEvent — append-only, seq-ordered event log entry (R-T1/R-T4).
|
|
40
|
+
/// Envelope: { agentId, seq, type, turnId, payload, createdAt } (§2).
|
|
41
|
+
/// workspaceId is denormalized so the scope layer filters events without a
|
|
42
|
+
/// join through agent_sessions.
|
|
43
|
+
model ConversationEvent {
|
|
44
|
+
id String @id @default(dbgenerated("('convev_'::text || (uuidv7())::text)")) @db.Text
|
|
45
|
+
sessionId String @map("session_id")
|
|
46
|
+
workspaceId String @map("workspace_id")
|
|
47
|
+
agentId String @map("agent_id")
|
|
48
|
+
seq Int
|
|
49
|
+
type String
|
|
50
|
+
turnId String? @map("turn_id")
|
|
51
|
+
payload Json
|
|
52
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
53
|
+
|
|
54
|
+
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
55
|
+
|
|
56
|
+
@@unique([sessionId, seq])
|
|
57
|
+
@@index([workspaceId])
|
|
58
|
+
@@map("conversation_events")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// SessionReadReceipt — per-user read position for a session (R-T7 mark-read;
|
|
62
|
+
/// drives unread badges). userId is a stable auth-namespace ID — no FK.
|
|
63
|
+
model SessionReadReceipt {
|
|
64
|
+
id String @id @default(dbgenerated("('receipt_'::text || (uuidv7())::text)")) @db.Text
|
|
65
|
+
sessionId String @map("session_id")
|
|
66
|
+
workspaceId String @map("workspace_id")
|
|
67
|
+
userId String @map("user_id")
|
|
68
|
+
lastReadSeq Int @default(0) @map("last_read_seq")
|
|
69
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
70
|
+
|
|
71
|
+
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
72
|
+
|
|
73
|
+
@@unique([sessionId, userId])
|
|
74
|
+
@@index([workspaceId])
|
|
75
|
+
@@map("session_read_receipts")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Artifact — a session's produced output (first type: text/document, #3846).
|
|
79
|
+
model Artifact {
|
|
80
|
+
id String @id @default(dbgenerated("('artif_'::text || (uuidv7())::text)")) @db.Text
|
|
81
|
+
sessionId String @map("session_id")
|
|
82
|
+
workspaceId String @map("workspace_id")
|
|
83
|
+
type String @default("text/document")
|
|
84
|
+
title String
|
|
85
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
86
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
87
|
+
|
|
88
|
+
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
89
|
+
versions ArtifactVersion[]
|
|
90
|
+
|
|
91
|
+
@@index([sessionId])
|
|
92
|
+
@@index([workspaceId])
|
|
93
|
+
@@map("artifacts")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// ArtifactVersion — auto-saved version history; exactly one isCurrent per
|
|
97
|
+
/// artifact (enforced at the service layer on save/restore).
|
|
98
|
+
model ArtifactVersion {
|
|
99
|
+
id String @id @default(dbgenerated("('artiv_'::text || (uuidv7())::text)")) @db.Text
|
|
100
|
+
artifactId String @map("artifact_id")
|
|
101
|
+
workspaceId String @map("workspace_id")
|
|
102
|
+
content Json
|
|
103
|
+
isCurrent Boolean @default(true) @map("is_current")
|
|
104
|
+
savedAt DateTime @default(now()) @map("saved_at")
|
|
105
|
+
|
|
106
|
+
artifact Artifact @relation(fields: [artifactId], references: [id], onDelete: Cascade)
|
|
107
|
+
|
|
108
|
+
@@index([artifactId, savedAt(sort: Desc)])
|
|
109
|
+
@@index([workspaceId])
|
|
110
|
+
@@map("artifact_versions")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Usage-billing metering (FEAT-047 P1.S1, #3709 — Epic #3704)
|
|
115
|
+
//
|
|
116
|
+
// The metering event + its org-level aggregates. Per ADR 0009 D2 (roadmap §4),
|
|
117
|
+
// the metering event + UsagePeriod + UsageBillingReport live in `content`;
|
|
118
|
+
// balances/plans (S2) live in auth/config. organizationId/workspaceId/userId
|
|
119
|
+
// are plain String stable-ids — no @relation into auth (ADR 0009 invariant 1).
|
|
120
|
+
// Field roster ported from docs/research/wt-billing-capture/schema.md, modeled
|
|
121
|
+
// on WT's TraceLog billing columns (spec D1), with workspaceId/userId added to
|
|
122
|
+
// the event grain (spec D2). Cross-namespace assembly (e.g. resolving an
|
|
123
|
+
// event's owning Workspace) routes through apps/api/src/services/.
|
|
124
|
+
// ============================================================================
|
|
125
|
+
|
|
126
|
+
/// ModelTier — the pricing tier at event time. BASE is unlimited/free-ish;
|
|
127
|
+
/// PREMIUM/FRONTIER consume credits. Defined here (S1, the dependency root)
|
|
128
|
+
/// per spec OQ-1 — S2's LlmModel references the same enum through the merged
|
|
129
|
+
/// prismaSchemaFolder (one logical schema; an enum reference needs no
|
|
130
|
+
/// @relation, so this is not a cross-namespace FK).
|
|
131
|
+
enum ModelTier {
|
|
132
|
+
BASE
|
|
133
|
+
PREMIUM
|
|
134
|
+
FRONTIER
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// UsageBillingStatus — UsageBillingReport lifecycle: PENDING → REPORTED →
|
|
138
|
+
/// INVOICED → PAID, or FAILED.
|
|
139
|
+
enum UsageBillingStatus {
|
|
140
|
+
PENDING
|
|
141
|
+
REPORTED
|
|
142
|
+
INVOICED
|
|
143
|
+
PAID
|
|
144
|
+
FAILED
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// UsageEvent — one row per billable AI call (the metering event; modeled on
|
|
148
|
+
/// WT's TraceLog billing columns, spec D1). org+workspace+user grain (spec
|
|
149
|
+
/// D2); all three scope ids are plain String stable-ids — no cross-namespace
|
|
150
|
+
/// @relation (ADR 0009 invariant 1, spec D3). The three attribution portion
|
|
151
|
+
/// columns are the load-bearing billing split; only onDemandPortionCents is
|
|
152
|
+
/// ever reported to Stripe (P4, spec D4). completionStatus 'success' rows
|
|
153
|
+
/// drive billing — aggregation (P2) filters on it.
|
|
154
|
+
model UsageEvent {
|
|
155
|
+
id String @id @default(dbgenerated("('usagev_'::text || (uuidv7())::text)")) @db.Text
|
|
156
|
+
organizationId String @map("organization_id")
|
|
157
|
+
workspaceId String @map("workspace_id")
|
|
158
|
+
userId String @map("user_id")
|
|
159
|
+
|
|
160
|
+
provider String
|
|
161
|
+
model String
|
|
162
|
+
|
|
163
|
+
// Cache-aware token breakdown.
|
|
164
|
+
inputTokensCacheWrite Int @map("input_tokens_cache_write")
|
|
165
|
+
inputTokensNoCacheWrite Int @map("input_tokens_no_cache_write")
|
|
166
|
+
cacheReadTokens Int @map("cache_read_tokens")
|
|
167
|
+
outputTokens Int @map("output_tokens")
|
|
168
|
+
thinkingTokens Int @map("thinking_tokens")
|
|
169
|
+
totalTokens Int @map("total_tokens")
|
|
170
|
+
|
|
171
|
+
providerCostUsd Decimal @map("provider_cost_usd") @db.Decimal(10, 6)
|
|
172
|
+
billedCostUsd Decimal @map("billed_cost_usd") @db.Decimal(10, 6)
|
|
173
|
+
markupPercent Decimal @map("markup_percent") @db.Decimal(5, 2)
|
|
174
|
+
|
|
175
|
+
modelTier ModelTier @default(BASE) @map("model_tier")
|
|
176
|
+
wasAutoModel Boolean @map("was_auto_model")
|
|
177
|
+
|
|
178
|
+
// Attribution split — billedCostUsd apportioned across the three buckets;
|
|
179
|
+
// only onDemandPortionCents is ever Stripe-metered (P4, spec D4).
|
|
180
|
+
includedPortionCents Int @default(0) @map("included_portion_cents")
|
|
181
|
+
creditBalancePortionCents Int @default(0) @map("credit_balance_portion_cents")
|
|
182
|
+
onDemandPortionCents Int @default(0) @map("on_demand_portion_cents")
|
|
183
|
+
|
|
184
|
+
completionStatus String @map("completion_status")
|
|
185
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
186
|
+
|
|
187
|
+
@@index([organizationId, createdAt])
|
|
188
|
+
@@index([workspaceId, createdAt])
|
|
189
|
+
@@index([userId, createdAt])
|
|
190
|
+
@@index([provider, model])
|
|
191
|
+
@@index([modelTier, wasAutoModel])
|
|
192
|
+
@@map("usage_events")
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// UsagePeriod — one row per (organizationId, periodStart); the org-scoped
|
|
196
|
+
/// spend/tier rollup the P2 write path upserts as UsageEvent rows land.
|
|
197
|
+
/// Carries the event-grain roll-up support for the Org → Workspace → User
|
|
198
|
+
/// aggregation (spec D2) — whether this model itself gains per-workspace
|
|
199
|
+
/// sub-rows is a P2 decision, not a P1 schema requirement (spec OQ-2).
|
|
200
|
+
model UsagePeriod {
|
|
201
|
+
id String @id @default(dbgenerated("('usagper_'::text || (uuidv7())::text)")) @db.Text
|
|
202
|
+
organizationId String @map("organization_id")
|
|
203
|
+
|
|
204
|
+
periodStart DateTime @map("period_start")
|
|
205
|
+
periodEnd DateTime @map("period_end")
|
|
206
|
+
|
|
207
|
+
tokensConsumed BigInt @default(0) @map("tokens_consumed")
|
|
208
|
+
baseTokensConsumed BigInt @default(0) @map("base_tokens_consumed")
|
|
209
|
+
premiumTokensConsumed BigInt @default(0) @map("premium_tokens_consumed")
|
|
210
|
+
frontierTokensConsumed BigInt @default(0) @map("frontier_tokens_consumed")
|
|
211
|
+
|
|
212
|
+
usageSpendCents Int @default(0) @map("usage_spend_cents")
|
|
213
|
+
|
|
214
|
+
@@unique([organizationId, periodStart])
|
|
215
|
+
@@index([baseTokensConsumed, premiumTokensConsumed, frontierTokensConsumed], map: "usage_periods_tier_tracking_idx")
|
|
216
|
+
@@map("usage_periods")
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// UsageBillingReport — one per (organizationId, periodStart, periodEnd); the
|
|
220
|
+
/// Stripe-reporting unit rolled up from UsageEvent by P2 aggregation. Only
|
|
221
|
+
/// onDemandCents is ever reported to Stripe (P4, spec D4) — includedCents/
|
|
222
|
+
/// creditBalanceCents are informational attribution, never metered.
|
|
223
|
+
model UsageBillingReport {
|
|
224
|
+
id String @id @default(dbgenerated("('usagrept_'::text || (uuidv7())::text)")) @db.Text
|
|
225
|
+
organizationId String @map("organization_id")
|
|
226
|
+
|
|
227
|
+
periodStart DateTime @map("period_start")
|
|
228
|
+
periodEnd DateTime @map("period_end")
|
|
229
|
+
|
|
230
|
+
// AGGREGATE precision (#3738 H2) — these hold a MONTH of events, not one
|
|
231
|
+
// event, so they must NOT reuse UsageEvent's per-event widths. At the
|
|
232
|
+
// original Decimal(10,6)/Int, an org whose month crossed $9,999.999999 (or
|
|
233
|
+
// 2.15B tokens) overflowed the column, the upsert threw, and the report was
|
|
234
|
+
// never written on ANY subsequent run — silently stalling revenue for the
|
|
235
|
+
// largest customers, worsening monotonically. Decimal(20,6) ≈ $1e14 and int8
|
|
236
|
+
// give headroom far above the service's own MAX_TOTAL_USD / token caps, so
|
|
237
|
+
// the service's loud `aggregate_overflow` assertion is always the gate and a
|
|
238
|
+
// silent DB overflow is unreachable. See usage-aggregation.ts § Bounds.
|
|
239
|
+
totalProviderCost Decimal @map("total_provider_cost") @db.Decimal(20, 6)
|
|
240
|
+
totalMarkupAmount Decimal @map("total_markup_amount") @db.Decimal(20, 6)
|
|
241
|
+
totalBilledAmount Decimal @map("total_billed_amount") @db.Decimal(20, 6)
|
|
242
|
+
|
|
243
|
+
totalTokens BigInt @map("total_tokens")
|
|
244
|
+
eventCount Int @map("event_count")
|
|
245
|
+
|
|
246
|
+
// Attribution split — only onDemandCents is ever Stripe-metered (P4, spec D4).
|
|
247
|
+
includedCents Int @default(0) @map("included_cents")
|
|
248
|
+
creditBalanceCents Int @default(0) @map("credit_balance_cents")
|
|
249
|
+
onDemandCents Int @default(0) @map("on_demand_cents")
|
|
250
|
+
|
|
251
|
+
stripeUsageRecordId String? @map("stripe_usage_record_id")
|
|
252
|
+
reportedToStripeAt DateTime? @map("reported_to_stripe_at")
|
|
253
|
+
stripeInvoiceId String? @map("stripe_invoice_id")
|
|
254
|
+
|
|
255
|
+
status UsageBillingStatus @default(PENDING)
|
|
256
|
+
errorMessage String? @map("error_message")
|
|
257
|
+
|
|
258
|
+
@@unique([organizationId, periodStart, periodEnd], map: "usage_billing_reports_period_key")
|
|
259
|
+
@@index([organizationId, status])
|
|
260
|
+
@@index([status])
|
|
261
|
+
@@index([periodStart])
|
|
262
|
+
@@map("usage_billing_reports")
|
|
263
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file listing-diagnostic.ts
|
|
3
|
+
* @description The ONE scoring RULE every project-listing diagnostic shares
|
|
4
|
+
* (#5001, reworked by #5036 D4) — the template's Q3–Q6 resolved by option
|
|
5
|
+
* VALUE, never by option position.
|
|
6
|
+
*
|
|
7
|
+
* The mechanism (#5036 D4, "scoring by value, not position"): the generated
|
|
8
|
+
* flow emits each scored option under the FOUNDER'S OWN `option.value`, and
|
|
9
|
+
* the register's `optionWeights` map is keyed on those same values. Q3/Q4 are
|
|
10
|
+
* founder-authored, so their weights map is built PER LISTING from that
|
|
11
|
+
* listing's own options — the WEIGHT still comes from the founder's declared
|
|
12
|
+
* order via `listingOptionPoints(index, arity)`, but it is stored against the
|
|
13
|
+
* value rather than an index. Q5/Q6 carry fixed values
|
|
14
|
+
* (`projectListingQuestionsSchema`, #4998), so their weights are declared once
|
|
15
|
+
* here, shared by every listing.
|
|
16
|
+
*
|
|
17
|
+
* What that buys, and why the old `q{n}:{i}of{k}` encoding had to go: meaning
|
|
18
|
+
* now travels with the value. A register may reorder or insert options and an
|
|
19
|
+
* already-stored answer keeps scoring what it always meant, so the subset a
|
|
20
|
+
* surface renders no longer has to be a prefix of the register's list — which
|
|
21
|
+
* is what unblocks the shared team-composition value set (#5036 D3). It also
|
|
22
|
+
* dissolves the "shared register needs a shared value universe" problem
|
|
23
|
+
* (docs/lessons/2026-09-07-…): the generated experiment's `questionSet` now
|
|
24
|
+
* declares the listing's OWN options, so
|
|
25
|
+
* `waitlistExperimentConfigSchema`'s "every weights key must be a declared
|
|
26
|
+
* option" cross-validation is satisfied by construction rather than by
|
|
27
|
+
* declaring a finite universe of positional tokens.
|
|
28
|
+
*
|
|
29
|
+
* What is shared is therefore the RULE and the shape — same point names, same
|
|
30
|
+
* `total` sum, same tier floors, same 0–12 range — not a single frozen
|
|
31
|
+
* constant. Two listings' registers differ only in the Q3/Q4 KEYS, which are
|
|
32
|
+
* the founders' own option values.
|
|
33
|
+
*
|
|
34
|
+
* Q5 runs STRONGEST-FIRST, deliberately. `projectListingQuestionsSchema` pins
|
|
35
|
+
* Q5's values as `urgent | planning | exploring`, which is urgency descending,
|
|
36
|
+
* while the positional rule scores index 0 lowest — so the old positional
|
|
37
|
+
* encoding scored `urgent` 0 and `exploring` 3, backwards, a standing defect
|
|
38
|
+
* recorded in docs/lessons/2026-09-07-q5-fixed-order-is-strongest-first.md.
|
|
39
|
+
* Scoring by value is exactly the mechanism that resolves it: the weights
|
|
40
|
+
* below name `urgent` 3 and `exploring` 0 explicitly, with no reordering of
|
|
41
|
+
* the founder's rendered options and no per-question special case hidden
|
|
42
|
+
* inside a positional formula. That lesson's open question is closed here.
|
|
43
|
+
* Q6 (`low → critical`) is genuinely weakest-first and keeps 0 → 3.
|
|
44
|
+
*
|
|
45
|
+
* Q1 (contact), Q2 (segment), Q7 (free text) and the extras carry no points —
|
|
46
|
+
* they are absent from `points` entirely, so they contribute 0 by
|
|
47
|
+
* construction rather than by a zero weight that could drift.
|
|
48
|
+
*
|
|
49
|
+
* This module is data + the pure functions that generate it — it imports only
|
|
50
|
+
* TYPES from `@working-theory/validation` and introduces no dependency from
|
|
51
|
+
* `database/` toward `apps/*` (architecture.yaml's import boundary), exactly
|
|
52
|
+
* like `presets.ts` beside it. The rule lives here and only here; the
|
|
53
|
+
* projection (`apps/api/src/services/project-listing-flow.ts`) calls
|
|
54
|
+
* `buildListingDiagnosticScoring` rather than re-deriving weights, so the
|
|
55
|
+
* register and the flow can never disagree about what an answer is worth.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import type {
|
|
59
|
+
ProjectListingQuestions,
|
|
60
|
+
WaitlistScoring,
|
|
61
|
+
} from "@working-theory/validation";
|
|
62
|
+
|
|
63
|
+
/** The four scored template questions, in template order. */
|
|
64
|
+
export const LISTING_DIAGNOSTIC_SCORED_QUESTIONS = ["q3", "q4", "q5", "q6"] as const;
|
|
65
|
+
export type ListingDiagnosticScoredQuestion =
|
|
66
|
+
(typeof LISTING_DIAGNOSTIC_SCORED_QUESTIONS)[number];
|
|
67
|
+
|
|
68
|
+
/** The highest score any one question can contribute. */
|
|
69
|
+
export const LISTING_DIAGNOSTIC_MAX_QUESTION_POINTS = 3;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The weight of option `index` (zero-based) of `arity` options —
|
|
73
|
+
* `round(3·i/(k−1))`, so the founder's first option always scores 0 and their
|
|
74
|
+
* last always scores 3, whatever the arity. This is still how a
|
|
75
|
+
* founder-authored question's weights are DERIVED (the founder orders their
|
|
76
|
+
* options weakest→strongest); what changed in #5036 D4 is where the result is
|
|
77
|
+
* STORED — against the option's value, not its index.
|
|
78
|
+
*
|
|
79
|
+
* A single-option question (`arity <= 1`) has no spread to score and yields 0;
|
|
80
|
+
* the template never produces one, and this guard exists so the expression can
|
|
81
|
+
* never divide by zero.
|
|
82
|
+
*/
|
|
83
|
+
export function listingOptionPoints(index: number, arity: number): number {
|
|
84
|
+
if (arity <= 1) return 0;
|
|
85
|
+
return Math.round((LISTING_DIAGNOSTIC_MAX_QUESTION_POINTS * index) / (arity - 1));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `optionWeights` for one founder-authored question (Q3/Q4), keyed by the
|
|
90
|
+
* founder's own option values and weighted by their declared order.
|
|
91
|
+
*/
|
|
92
|
+
export function listingDiagnosticOptionWeights(
|
|
93
|
+
options: readonly { value: string }[],
|
|
94
|
+
): Record<string, number> {
|
|
95
|
+
const weights: Record<string, number> = {};
|
|
96
|
+
options.forEach((option, index) => {
|
|
97
|
+
weights[option.value] = listingOptionPoints(index, options.length);
|
|
98
|
+
});
|
|
99
|
+
return weights;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Q5's fixed urgency weights — STRONGEST FIRST, against the positional rule
|
|
104
|
+
* and deliberately so (see the file header). `urgent` is the strongest buying
|
|
105
|
+
* signal the question can carry and scores 3; `exploring` is the weakest and
|
|
106
|
+
* scores 0. The three weights are the same multiset a 3-option positional
|
|
107
|
+
* question produces (`{0, 2, 3}`), so the 0–12 total range is unchanged.
|
|
108
|
+
*/
|
|
109
|
+
export const LISTING_DIAGNOSTIC_Q5_WEIGHTS: Readonly<Record<string, number>> = {
|
|
110
|
+
urgent: 3,
|
|
111
|
+
planning: 2,
|
|
112
|
+
exploring: 0,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Q6's fixed band weights — weakest first, matching the schema's own
|
|
117
|
+
* `low → critical` low→high ordering, so these are exactly what the positional
|
|
118
|
+
* rule produced for a 4-option question.
|
|
119
|
+
*/
|
|
120
|
+
export const LISTING_DIAGNOSTIC_Q6_WEIGHTS: Readonly<Record<string, number>> = {
|
|
121
|
+
low: 0,
|
|
122
|
+
medium: 1,
|
|
123
|
+
high: 2,
|
|
124
|
+
critical: 3,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** The four tier keys, strongest first — the order `tiers` is evaluated in. */
|
|
128
|
+
export const LISTING_DIAGNOSTIC_TIERS = ["now", "soon", "later", "curious"] as const;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The inclusive lower bound of each tier over the 0–12 total (4 questions ×
|
|
132
|
+
* 3 points): `now` 10–12, `soon` 7–9, `later` 4–6, `curious` 0–3. Ordered
|
|
133
|
+
* strongest-first because `evaluateWaitlistScoring` takes the FIRST matching
|
|
134
|
+
* tier, so a `gte` ladder read top-down is total and unambiguous.
|
|
135
|
+
*/
|
|
136
|
+
const TIER_FLOORS: Readonly<Record<(typeof LISTING_DIAGNOSTIC_TIERS)[number], number>> = {
|
|
137
|
+
now: 10,
|
|
138
|
+
soon: 7,
|
|
139
|
+
later: 4,
|
|
140
|
+
curious: 0,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build one listing's register from its own `questions` block. Embedded
|
|
145
|
+
* verbatim into that listing's generated `WaitlistExperiment.config` —
|
|
146
|
+
* `waitlistExperimentConfigSchema` carries the scoring block by value and
|
|
147
|
+
* offers no by-reference slot.
|
|
148
|
+
*
|
|
149
|
+
* Every listing gets the same point names, the same `total` sum, the same
|
|
150
|
+
* tier ladder and the same 0–12 range; only the Q3/Q4 weight KEYS differ,
|
|
151
|
+
* because those keys are the founder's own option values (#5036 D4).
|
|
152
|
+
*/
|
|
153
|
+
export function buildListingDiagnosticScoring(
|
|
154
|
+
questions: ProjectListingQuestions,
|
|
155
|
+
): WaitlistScoring {
|
|
156
|
+
const weights: Record<ListingDiagnosticScoredQuestion, Record<string, number>> = {
|
|
157
|
+
q3: listingDiagnosticOptionWeights(questions.q3.options),
|
|
158
|
+
q4: listingDiagnosticOptionWeights(questions.q4.options),
|
|
159
|
+
q5: { ...LISTING_DIAGNOSTIC_Q5_WEIGHTS },
|
|
160
|
+
q6: { ...LISTING_DIAGNOSTIC_Q6_WEIGHTS },
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
version: 1,
|
|
165
|
+
points: LISTING_DIAGNOSTIC_SCORED_QUESTIONS.map((question) => ({
|
|
166
|
+
name: `${question}_points`,
|
|
167
|
+
question,
|
|
168
|
+
kind: "optionWeights" as const,
|
|
169
|
+
weights: weights[question],
|
|
170
|
+
})),
|
|
171
|
+
scores: {
|
|
172
|
+
total: {
|
|
173
|
+
sum: LISTING_DIAGNOSTIC_SCORED_QUESTIONS.map((q) => `${q}_points`),
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
tiers: LISTING_DIAGNOSTIC_TIERS.map((key) => ({
|
|
177
|
+
key,
|
|
178
|
+
when: { score: "total", gte: TIER_FLOORS[key] },
|
|
179
|
+
})),
|
|
180
|
+
default: "curious",
|
|
181
|
+
};
|
|
182
|
+
}
|