create-ailk 0.23.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -33
- package/component-catalog.json +3 -3
- package/component-catalog.md +3 -3
- package/dist/add-module.js +10 -3
- package/dist/add-route.d.ts +12 -1
- package/dist/add-route.js +38 -4
- package/dist/cli.js +12 -7
- package/dist/derive-architecture.d.ts +3 -4
- package/dist/derive-architecture.js +75 -22
- package/dist/lib/design-checks.d.ts +9 -4
- package/dist/lib/design-checks.js +13 -7
- package/dist/lib/extract-module-bundle.js +29 -2
- package/dist/lib/fetch-template-snapshot.d.ts +1 -1
- package/dist/lib/fetch-template-snapshot.js +7 -3
- package/dist/lib/import-graph.d.ts +1 -1
- package/dist/lib/import-graph.js +4 -4
- package/dist/lib/module-license-gate.d.ts +12 -1
- package/dist/lib/module-license-gate.js +34 -12
- package/dist/lib/scaffold-render-sweep.d.ts +33 -0
- package/dist/lib/scaffold-render-sweep.js +45 -0
- package/dist/module-architecture.d.ts +20 -3
- package/dist/module-architecture.js +122 -538
- package/dist/paid-sections.d.ts +25 -1
- package/dist/paid-sections.js +88 -3
- package/dist/surfaces.js +10 -2
- package/dist/sync-routes.d.ts +30 -4
- package/dist/sync-routes.js +271 -18
- package/dist/template-record.d.ts +14 -4
- package/dist/template-record.js +23 -5
- package/dist/topology.d.ts +16 -3
- package/dist/topology.js +31 -19
- package/dist/update.d.ts +10 -1
- package/dist/update.js +540 -16
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/templates/.claude/cloudparity-repo-hooks.json +5 -0
- package/templates/.claude/hooks/template-guard.sh +137 -0
- package/templates/.claude/rules/extending.md +106 -0
- package/templates/.claude/rules/loop-contract.md +2 -2
- package/templates/.claude/settings.json +11 -0
- package/templates/.env.example +9 -5
- package/templates/.github/workflows/ci.yml +7 -4
- package/templates/CLAUDE.md +15 -3
- package/templates/CONVENTIONS.md +8 -10
- package/templates/README.md +14 -13
- package/templates/apps/api/.env.example +9 -2
- package/templates/apps/api/CLAUDE.md +122 -82
- package/templates/apps/api/jest.config.cjs +15 -5
- package/templates/apps/api/package.json +16 -20
- package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
- package/templates/apps/api/src/index.ts +1 -1
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +7 -4
- package/templates/apps/api/src/modules/commerce.ts +10 -0
- package/templates/apps/api/src/modules/content-write.ts +10 -0
- package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
- package/templates/apps/api/src/modules/scheduling.ts +10 -0
- package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
- package/templates/apps/api/src/modules/workspace.ts +10 -0
- package/templates/apps/api/src/server.ts +117 -1857
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/CLAUDE.md +77 -24
- package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
- package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
- package/templates/apps/mcp/jest.config.cjs +6 -1
- package/templates/apps/mcp/package.json +5 -12
- package/templates/apps/mcp/src/app.ts +8 -7
- package/templates/apps/mcp/src/index.ts +6 -34
- package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
- package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
- package/templates/apps/mcp/src/site.ts +12 -0
- package/templates/apps/web/.env.example +1 -1
- package/templates/apps/web/CLAUDE.md +21 -5
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +14 -4
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +28 -7
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
- package/templates/apps/web/app/api/revalidate/route.ts +3 -3
- package/templates/apps/web/auth.ts +2 -2
- package/templates/apps/web/jest.config.cjs +1 -0
- package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +7 -0
- package/templates/apps/web/lib/auth-email.ts +1 -1
- package/templates/apps/web/lib/magnet-routes.ts +3 -1
- package/templates/apps/web/lib/newsletter.actions.ts +9 -6
- package/templates/apps/web/lib/relay.ts +2 -2
- package/templates/apps/web/lib/release-scope.ts +47 -0
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
- package/templates/customizations.yaml +37 -0
- package/templates/database/prisma.config.ts +10 -14
- package/templates/database/seed-test.ts +2 -2
- package/templates/database/seed.ts +32 -5
- package/templates/package.json +12 -6
- package/templates/pnpm-workspace.yaml +0 -1
- package/templates/project.yaml +16 -8
- package/templates/tools/jest/reporters.cjs +65 -0
- package/templates/tools/lint-architecture.ts +757 -7
- package/templates/tsconfig.json +1 -2
- package/versions.json +48 -35
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
- package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
- package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
- package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
- package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
- package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
- package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
- package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
- package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
- package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
- package/templates/apps/api/src/bin/spokes.ts +0 -198
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
- package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
- package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
- package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
- package/templates/apps/api/src/config/index.ts +0 -596
- package/templates/apps/api/src/config/modules.ts +0 -83
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
- package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
- package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
- package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
- package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
- package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
- package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
- package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
- package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
- package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
- package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -292
- package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
- package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
- package/templates/apps/api/src/lib/agent-store.ts +0 -613
- package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
- package/templates/apps/api/src/lib/consent-store.ts +0 -363
- package/templates/apps/api/src/lib/content-adapter.ts +0 -112
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
- package/templates/apps/api/src/lib/error-serializer.ts +0 -164
- package/templates/apps/api/src/lib/errors.ts +0 -95
- package/templates/apps/api/src/lib/file-fallback.ts +0 -87
- package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
- package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
- package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
- package/templates/apps/api/src/lib/license-key-store.ts +0 -45
- package/templates/apps/api/src/lib/locales.ts +0 -201
- package/templates/apps/api/src/lib/outbound-http.ts +0 -127
- package/templates/apps/api/src/lib/prisma.ts +0 -21
- package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
- package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
- package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
- package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
- package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
- package/templates/apps/api/src/lib/site.ts +0 -254
- package/templates/apps/api/src/lib/slice-load.ts +0 -90
- package/templates/apps/api/src/lib/spoke-store.ts +0 -180
- package/templates/apps/api/src/lib/stripe.ts +0 -318
- package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
- package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
- package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
- package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
- package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
- package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
- package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
- package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
- package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
- package/templates/apps/api/src/middleware/auth.ts +0 -864
- package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
- package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -264
- package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
- package/templates/apps/api/src/openapi/index.ts +0 -18
- package/templates/apps/api/src/openapi/spec.ts +0 -1815
- package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
- package/templates/apps/api/src/routes/aeo/index.ts +0 -107
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
- package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
- package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
- package/templates/apps/api/src/routes/auth/user.ts +0 -305
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -325
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1300
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
- package/templates/apps/api/src/routes/checkout/index.ts +0 -34
- package/templates/apps/api/src/routes/checkout/session.ts +0 -171
- package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
- package/templates/apps/api/src/routes/consent/account.ts +0 -268
- package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
- package/templates/apps/api/src/routes/consent/index.ts +0 -26
- package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
- package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
- package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
- package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
- package/templates/apps/api/src/routes/content/get-page.ts +0 -268
- package/templates/apps/api/src/routes/content/index.ts +0 -49
- package/templates/apps/api/src/routes/content/list-pages.ts +0 -95
- package/templates/apps/api/src/routes/content/list-types.ts +0 -19
- package/templates/apps/api/src/routes/content/offerings.ts +0 -184
- package/templates/apps/api/src/routes/content/pricing.ts +0 -176
- package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
- package/templates/apps/api/src/routes/content/search.ts +0 -129
- package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
- package/templates/apps/api/src/routes/content/slug.ts +0 -9
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
- package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
- package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
- package/templates/apps/api/src/routes/leads/index.ts +0 -573
- package/templates/apps/api/src/routes/leads/list.ts +0 -104
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -480
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -465
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
- package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
- package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
- package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
- package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
- package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
- package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
- package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
- package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
- package/templates/apps/api/src/routes/requests/index.ts +0 -54
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
- package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
- package/templates/apps/api/src/routes/webhooks/README.md +0 -398
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -448
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -808
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
- package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
- package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2026
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -528
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
- package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
- package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
- package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -867
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
- package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
- package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
- package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
- package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1176
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -444
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -154
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
- package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
- package/templates/apps/api/src/services/abandon-email.ts +0 -313
- package/templates/apps/api/src/services/aeo-score.ts +0 -325
- package/templates/apps/api/src/services/answer-distributions.ts +0 -138
- package/templates/apps/api/src/services/audit-log.ts +0 -961
- package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
- package/templates/apps/api/src/services/consent.ts +0 -834
- package/templates/apps/api/src/services/default-tenant.ts +0 -416
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -755
- package/templates/apps/api/src/services/dunning-policy.ts +0 -294
- package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
- package/templates/apps/api/src/services/entitlements.ts +0 -338
- package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
- package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
- package/templates/apps/api/src/services/lead-promotion.ts +0 -160
- package/templates/apps/api/src/services/lead-routing.ts +0 -1372
- package/templates/apps/api/src/services/leads-inbox.ts +0 -312
- package/templates/apps/api/src/services/listing-config.ts +0 -178
- package/templates/apps/api/src/services/magnet-completion.ts +0 -242
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -295
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -868
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -396
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -681
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
- package/templates/apps/api/src/services/offerings/catalog.ts +0 -308
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -385
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
- package/templates/apps/api/src/services/offerings/connection.ts +0 -1003
- package/templates/apps/api/src/services/offerings/index.ts +0 -614
- package/templates/apps/api/src/services/offerings/source.ts +0 -140
- package/templates/apps/api/src/services/offerings/store.ts +0 -150
- package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
- package/templates/apps/api/src/services/person.ts +0 -1188
- package/templates/apps/api/src/services/plan-offerings.ts +0 -125
- package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
- package/templates/apps/api/src/services/project-listings.ts +0 -2073
- package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
- package/templates/apps/api/src/services/request-confirm.ts +0 -698
- package/templates/apps/api/src/services/revalidation.ts +0 -235
- package/templates/apps/api/src/services/slack-notify.ts +0 -291
- package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
- package/templates/apps/api/src/services/usage-credits.ts +0 -340
- package/templates/apps/api/src/services/verified-email.ts +0 -71
- package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
- package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
- package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
- package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
- package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
- package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
- package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
- package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
- package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
- package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
- package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
- package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1151
- package/templates/apps/mcp/__tests__/index.test.ts +0 -195
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -65
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -187
- package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
- package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -165
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
- package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -228
- package/templates/apps/mcp/__tests__/locale.test.ts +0 -135
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
- package/templates/apps/mcp/__tests__/read_content.test.ts +0 -474
- package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -225
- package/templates/apps/mcp/__tests__/search_content.test.ts +0 -256
- package/templates/apps/mcp/__tests__/server.test.ts +0 -388
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -307
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -223
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
- package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
- package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
- package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
- package/templates/apps/mcp/src/abuse/index.ts +0 -342
- package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
- package/templates/apps/mcp/src/boot.ts +0 -167
- package/templates/apps/mcp/src/config/modules.ts +0 -43
- package/templates/apps/mcp/src/http-transport.ts +0 -526
- package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
- package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
- package/templates/apps/mcp/src/lib/content-adapter.ts +0 -142
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -139
- package/templates/apps/mcp/src/lib/errors.ts +0 -61
- package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
- package/templates/apps/mcp/src/lib/locales.ts +0 -171
- package/templates/apps/mcp/src/lib/prisma.ts +0 -39
- package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
- package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
- package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
- package/templates/apps/mcp/src/request-context.ts +0 -52
- package/templates/apps/mcp/src/server.ts +0 -343
- package/templates/apps/mcp/src/serverless-app.ts +0 -88
- package/templates/apps/mcp/src/tools/audit.ts +0 -127
- package/templates/apps/mcp/src/tools/capture_lead.ts +0 -64
- package/templates/apps/mcp/src/tools/create_project.ts +0 -134
- package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
- package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
- package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
- package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
- package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
- package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
- package/templates/apps/mcp/src/tools/index.ts +0 -137
- package/templates/apps/mcp/src/tools/list_pages.ts +0 -74
- package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
- package/templates/apps/mcp/src/tools/proposals.ts +0 -27
- package/templates/apps/mcp/src/tools/purchase.ts +0 -205
- package/templates/apps/mcp/src/tools/read_content.ts +0 -75
- package/templates/apps/mcp/src/tools/request_callback.ts +0 -59
- package/templates/apps/mcp/src/tools/search_content.ts +0 -67
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -57
- package/templates/apps/mcp/src/workspace-server.ts +0 -224
- package/templates/database/CHANGELOG.md +0 -961
- package/templates/database/LICENSE +0 -202
- package/templates/database/NOTICE +0 -2
- package/templates/database/README.md +0 -243
- package/templates/database/__tests__/agents.db.test.ts +0 -320
- package/templates/database/__tests__/appointment.db.test.ts +0 -106
- package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
- package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
- package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
- package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
- package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
- package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
- package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
- package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
- package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
- package/templates/database/__tests__/flows-presets.test.ts +0 -173
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
- package/templates/database/__tests__/member-area.db.test.ts +0 -257
- package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
- package/templates/database/__tests__/member-contract.db.test.ts +0 -413
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
- package/templates/database/__tests__/offerings-migration.test.ts +0 -197
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
- package/templates/database/__tests__/offerings.db.test.ts +0 -303
- package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
- package/templates/database/__tests__/person-contact.db.test.ts +0 -563
- package/templates/database/__tests__/person-notes.db.test.ts +0 -249
- package/templates/database/__tests__/person.db.test.ts +0 -586
- package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
- package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
- package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
- package/templates/database/__tests__/seed.test.ts +0 -240
- package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
- package/templates/database/__tests__/tenancy.test.ts +0 -141
- package/templates/database/__tests__/user-cascade.test.ts +0 -140
- package/templates/database/_gitignore +0 -8
- package/templates/database/auth/.gitkeep +0 -0
- package/templates/database/auth/schema.prisma +0 -641
- package/templates/database/config/.gitkeep +0 -0
- package/templates/database/config/schema.prisma +0 -441
- package/templates/database/content/.gitkeep +0 -0
- package/templates/database/content/schema.prisma +0 -339
- package/templates/database/eslint.config.mjs +0 -69
- package/templates/database/flows/listing-diagnostic.ts +0 -182
- package/templates/database/flows/presets.ts +0 -315
- package/templates/database/inbox/.gitkeep +0 -0
- package/templates/database/inbox/README.md +0 -3
- package/templates/database/inbox/__tests__/schema.test.ts +0 -44
- package/templates/database/inbox/schema.prisma +0 -1447
- package/templates/database/jest.config.cjs +0 -36
- package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
- package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
- package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
- package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
- package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
- package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
- package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
- package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
- package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
- package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
- package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
- package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
- package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
- package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
- package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
- package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
- package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
- package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
- package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
- package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
- package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
- package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
- package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
- package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
- package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
- package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
- package/templates/database/migrations/migration_lock.toml +0 -3
- package/templates/database/ops/.gitkeep +0 -0
- package/templates/database/ops/schema.prisma +0 -43
- package/templates/database/package.json +0 -89
- package/templates/database/schema.prisma +0 -71
- package/templates/database/scripts/db-generate-locked.sh +0 -80
- package/templates/database/scripts/person-backfill.sql +0 -132
- package/templates/database/tenancy.ts +0 -183
- package/templates/database/tsconfig.build.json +0 -16
- package/templates/database/tsconfig.flows.json +0 -15
- package/templates/database/tsconfig.json +0 -16
- package/templates/database/tsconfig.tenancy.json +0 -14
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the ailkCreateProject MCP tool handler.
|
|
3
|
-
*
|
|
4
|
-
* Acceptance criteria verified here:
|
|
5
|
-
* - scaffoldInputSchema validates correct inputs
|
|
6
|
-
* - scaffoldInputSchema rejects invalid inputs (empty projectName, missing targetDir)
|
|
7
|
-
* - ailkCreateProject scaffolds into a tmp dir: package.json + ≥1 file created
|
|
8
|
-
* - ailkCreateProject fails cleanly when targetDir is non-empty (rejects with Error)
|
|
9
|
-
* - Output validates against scaffoldOutputSchema
|
|
10
|
-
* - No direct create-ailk import in apps/mcp (architectural guard)
|
|
11
|
-
*
|
|
12
|
-
* Hermetic: uses os.tmpdir() + unique subdir. No network calls.
|
|
13
|
-
* Integration tests pass CLI_PATH explicitly to ailkCreateProject (second arg)
|
|
14
|
-
* so they don't depend on process.cwd() being the repo root in Jest.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
existsSync,
|
|
19
|
-
mkdirSync,
|
|
20
|
-
mkdtempSync,
|
|
21
|
-
rmSync,
|
|
22
|
-
writeFileSync,
|
|
23
|
-
} from "node:fs";
|
|
24
|
-
import { tmpdir } from "node:os";
|
|
25
|
-
import { join, resolve } from "node:path";
|
|
26
|
-
|
|
27
|
-
import { scaffoldInputSchema, scaffoldOutputSchema } from "@working-theory/validation";
|
|
28
|
-
|
|
29
|
-
import { ailkCreateProject } from "../../src/tools/create_project.js";
|
|
30
|
-
|
|
31
|
-
// ── Input schema tests ────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
describe("scaffoldInputSchema", () => {
|
|
34
|
-
it("accepts valid input with targetDir and projectName", () => {
|
|
35
|
-
const result = scaffoldInputSchema.safeParse({
|
|
36
|
-
targetDir: "/tmp/my-site",
|
|
37
|
-
projectName: "my-site",
|
|
38
|
-
});
|
|
39
|
-
expect(result.success).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("accepts valid input with optional preset", () => {
|
|
43
|
-
const result = scaffoldInputSchema.safeParse({
|
|
44
|
-
targetDir: "/tmp/my-site",
|
|
45
|
-
projectName: "my-site",
|
|
46
|
-
preset: "default",
|
|
47
|
-
});
|
|
48
|
-
expect(result.success).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("rejects input missing targetDir", () => {
|
|
52
|
-
const result = scaffoldInputSchema.safeParse({
|
|
53
|
-
projectName: "my-site",
|
|
54
|
-
});
|
|
55
|
-
expect(result.success).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("rejects input with empty projectName", () => {
|
|
59
|
-
const result = scaffoldInputSchema.safeParse({
|
|
60
|
-
targetDir: "/tmp/my-site",
|
|
61
|
-
projectName: "",
|
|
62
|
-
});
|
|
63
|
-
expect(result.success).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("rejects input missing projectName", () => {
|
|
67
|
-
const result = scaffoldInputSchema.safeParse({
|
|
68
|
-
targetDir: "/tmp/my-site",
|
|
69
|
-
});
|
|
70
|
-
expect(result.success).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// ── Output schema tests ───────────────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
describe("scaffoldOutputSchema", () => {
|
|
77
|
-
it("accepts a valid success output", () => {
|
|
78
|
-
const result = scaffoldOutputSchema.safeParse({
|
|
79
|
-
targetDir: "/tmp/my-site",
|
|
80
|
-
filesCreated: 5,
|
|
81
|
-
});
|
|
82
|
-
expect(result.success).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("accepts zero filesCreated (empty template edge case)", () => {
|
|
86
|
-
const result = scaffoldOutputSchema.safeParse({
|
|
87
|
-
targetDir: "/tmp/my-site",
|
|
88
|
-
filesCreated: 0,
|
|
89
|
-
});
|
|
90
|
-
expect(result.success).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("rejects output missing targetDir", () => {
|
|
94
|
-
const result = scaffoldOutputSchema.safeParse({ filesCreated: 5 });
|
|
95
|
-
expect(result.success).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// ── Handler integration tests ─────────────────────────────────────────────────
|
|
100
|
-
|
|
101
|
-
// Resolve CLI_PATH relative to the repo root using __dirname traversal.
|
|
102
|
-
// __tests__/tools → __tests__ → apps/mcp → apps → repo root
|
|
103
|
-
const REPO_ROOT = resolve(__dirname, "../../../..");
|
|
104
|
-
const CLI_PATH = join(REPO_ROOT, "packages/create-ailk/dist/cli.js");
|
|
105
|
-
// NO_HAND_IMPLEMENT_OK: trunk fix for a red main (CI run 36386534464), operator-attended session.
|
|
106
|
-
// The CLI substitutes `workspace:*` versions from create-ailk's gitignored
|
|
107
|
-
// `versions.json`, which only `build-templates` writes ("empty before the first
|
|
108
|
-
// build"); with it absent every scaffold throws "no version found for …".
|
|
109
|
-
// Turbo gives this package's test no dependency on create-ailk's build-templates
|
|
110
|
-
// step, so whether the file exists here depends on shard packing. Every case
|
|
111
|
-
// that runs the CLI therefore skips — with a warning, never a failure — until
|
|
112
|
-
// both the CLI and its version map are on disk.
|
|
113
|
-
const VERSIONS_PATH = join(REPO_ROOT, "packages/create-ailk/versions.json");
|
|
114
|
-
|
|
115
|
-
/** True (after warning) when the CLI cannot scaffold in this checkout yet. */
|
|
116
|
-
function skipUnlessCliReady(): boolean {
|
|
117
|
-
if (!existsSync(CLI_PATH)) {
|
|
118
|
-
console.warn("[create_project.test] skipping integration test — CLI not built");
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
if (!existsSync(VERSIONS_PATH)) {
|
|
122
|
-
console.warn(
|
|
123
|
-
"[create_project.test] skipping integration test — run `pnpm --filter create-ailk run build-templates` first",
|
|
124
|
-
);
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
describe("ailkCreateProject handler", () => {
|
|
131
|
-
let scratch: string;
|
|
132
|
-
|
|
133
|
-
beforeAll(() => {
|
|
134
|
-
if (!existsSync(CLI_PATH)) {
|
|
135
|
-
console.warn(
|
|
136
|
-
`[create_project.test] create-ailk CLI not built: ${CLI_PATH} missing. ` +
|
|
137
|
-
"Run `pnpm --filter create-ailk build` first.",
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
beforeEach(() => {
|
|
143
|
-
scratch = mkdtempSync(join(tmpdir(), "ailk-mcp-scaffold-"));
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
afterEach(() => {
|
|
147
|
-
rmSync(scratch, { recursive: true, force: true });
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("scaffolds at least one file into a fresh tmp dir and reports targetDir", async () => {
|
|
151
|
-
if (skipUnlessCliReady()) return;
|
|
152
|
-
const target = join(scratch, "my-site");
|
|
153
|
-
const output = await ailkCreateProject(
|
|
154
|
-
{ targetDir: target, projectName: "my-site" },
|
|
155
|
-
CLI_PATH,
|
|
156
|
-
);
|
|
157
|
-
expect(output.targetDir).toBe(target);
|
|
158
|
-
expect(output.filesCreated).toBeGreaterThan(0);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("scaffolds package.json when full templates are built (build-templates required)", async () => {
|
|
162
|
-
if (skipUnlessCliReady()) return;
|
|
163
|
-
const TEMPLATES_DIR = join(REPO_ROOT, "packages/create-ailk/templates");
|
|
164
|
-
const PKG_JSON_IN_TEMPLATES = join(TEMPLATES_DIR, "package.json");
|
|
165
|
-
if (!existsSync(PKG_JSON_IN_TEMPLATES)) {
|
|
166
|
-
console.warn(
|
|
167
|
-
"[create_project.test] skipping package.json assertion — run `pnpm --filter create-ailk run build-templates` first",
|
|
168
|
-
);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const target = join(scratch, "my-site");
|
|
172
|
-
await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
|
|
173
|
-
expect(existsSync(join(target, "package.json"))).toBe(true);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("scaffolds app dir when full templates are built (build-templates required)", async () => {
|
|
177
|
-
if (skipUnlessCliReady()) return;
|
|
178
|
-
const TEMPLATES_DIR = join(REPO_ROOT, "packages/create-ailk/templates");
|
|
179
|
-
const PKG_JSON_IN_TEMPLATES = join(TEMPLATES_DIR, "package.json");
|
|
180
|
-
if (!existsSync(PKG_JSON_IN_TEMPLATES)) {
|
|
181
|
-
console.warn(
|
|
182
|
-
"[create_project.test] skipping app dir assertion — run `pnpm --filter create-ailk run build-templates` first",
|
|
183
|
-
);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const target = join(scratch, "my-site");
|
|
187
|
-
await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
|
|
188
|
-
// The template produces apps/web/app/ from the monorepo template snapshot.
|
|
189
|
-
const hasApp =
|
|
190
|
-
existsSync(join(target, "app")) ||
|
|
191
|
-
existsSync(join(target, "apps", "web", "app"));
|
|
192
|
-
expect(hasApp).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("returns at least one file (schema-typed page seed or README.md)", async () => {
|
|
196
|
-
if (skipUnlessCliReady()) return;
|
|
197
|
-
const target = join(scratch, "my-site");
|
|
198
|
-
await ailkCreateProject({ targetDir: target, projectName: "my-site" }, CLI_PATH);
|
|
199
|
-
// At minimum README.md is always emitted by the template.
|
|
200
|
-
expect(existsSync(join(target, "README.md"))).toBe(true);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("returns output conforming to scaffoldOutputSchema", async () => {
|
|
204
|
-
if (skipUnlessCliReady()) return;
|
|
205
|
-
const target = join(scratch, "my-site");
|
|
206
|
-
const output = await ailkCreateProject(
|
|
207
|
-
{ targetDir: target, projectName: "my-site" },
|
|
208
|
-
CLI_PATH,
|
|
209
|
-
);
|
|
210
|
-
const result = scaffoldOutputSchema.safeParse(output);
|
|
211
|
-
expect(result.success).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("rejects with a clear error when targetDir is non-empty", async () => {
|
|
215
|
-
if (skipUnlessCliReady()) return;
|
|
216
|
-
const target = join(scratch, "populated");
|
|
217
|
-
mkdirSync(target);
|
|
218
|
-
writeFileSync(join(target, "existing.txt"), "data");
|
|
219
|
-
|
|
220
|
-
// The create-ailk CLI exits non-zero with a ValidationError message when
|
|
221
|
-
// the target exists and is non-empty. The MCP handler rejects with a
|
|
222
|
-
// descriptive Error whose message matches the CLI stderr output.
|
|
223
|
-
await expect(
|
|
224
|
-
ailkCreateProject({ targetDir: target, projectName: "populated" }, CLI_PATH),
|
|
225
|
-
).rejects.toThrow(/non-empty|exists/i);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// ── Architectural guard ───────────────────────────────────────────────────────
|
|
230
|
-
|
|
231
|
-
describe("architectural guard — no create-ailk direct import", () => {
|
|
232
|
-
it("apps/mcp/src/tools/create_project.ts does not import create-ailk directly", async () => {
|
|
233
|
-
const { readFileSync } = await import("node:fs");
|
|
234
|
-
const { resolve: res } = await import("node:path");
|
|
235
|
-
const src = readFileSync(
|
|
236
|
-
res(__dirname, "../../src/tools/create_project.ts"),
|
|
237
|
-
"utf-8",
|
|
238
|
-
);
|
|
239
|
-
// Must not have a static import of create-ailk or its dist
|
|
240
|
-
expect(src).not.toMatch(/from ['"]create-ailk['"]/);
|
|
241
|
-
expect(src).not.toMatch(/require\s*\(\s*['"]create-ailk['"]/);
|
|
242
|
-
});
|
|
243
|
-
});
|
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the ailkPurchase MCP tool.
|
|
3
|
-
*
|
|
4
|
-
* Rewritten for #6001: three security reviews on PR #5991 flagged that an
|
|
5
|
-
* anonymous MCP caller could forward its own successUrl/cancelUrl/
|
|
6
|
-
* allowPromotionCodes to `checkout.createSession` — a phishing-amplification
|
|
7
|
-
* open redirect off the seller's real checkout — so the tool is now gated by
|
|
8
|
-
* `purchaseMcpInputSchema`, a `.strict()` `.pick()` of the shared
|
|
9
|
-
* `purchaseInputSchema` exposing exactly `productCode` + `quantity`. A call
|
|
10
|
-
* carrying any of the three excluded fields now FAILS validation rather than
|
|
11
|
-
* having them silently stripped, and both redirects are ALWAYS derived from
|
|
12
|
-
* `AILK_BASE_URL`.
|
|
13
|
-
*
|
|
14
|
-
* `purchaseInputSchema` itself is unchanged (#5971 shape, still shared with
|
|
15
|
-
* the Server Action / API route, which are not anonymous surfaces) — its own
|
|
16
|
-
* schema tests below stay as coverage of the SHARED schema, distinct from
|
|
17
|
-
* `purchaseMcpInputSchema`'s tests, which cover the tool's OWN gate.
|
|
18
|
-
*
|
|
19
|
-
* `__tests__/start_purchase.test.ts` owns the parity proof and the
|
|
20
|
-
* hosted-URL/unknown-offering behaviour; this suite keeps the schema coverage
|
|
21
|
-
* and the architectural guard alongside it.
|
|
22
|
-
*
|
|
23
|
-
* Acceptance criteria verified here:
|
|
24
|
-
* - purchaseInputSchema (the shared schema) accepts/rejects per the route's
|
|
25
|
-
* own validation — unchanged coverage, since the schema itself is
|
|
26
|
-
* byte-unchanged (#6001 AC3)
|
|
27
|
-
* - purchaseMcpInputSchema (the tool's own gate) accepts exactly
|
|
28
|
-
* productCode + quantity and REJECTS successUrl/cancelUrl/
|
|
29
|
-
* allowPromotionCodes outright (#6001 AC1)
|
|
30
|
-
* - purchaseOutputSchema validates the { url: string } live shape
|
|
31
|
-
* - ailkPurchase calls POST /v1/checkout/sessions through the api-client seam
|
|
32
|
-
* - ailkPurchase always derives successUrl/cancelUrl from AILK_BASE_URL, with
|
|
33
|
-
* and without the env var set, and never forwards allowPromotionCodes
|
|
34
|
-
* (#6001 AC2)
|
|
35
|
-
* - ailkPurchase returns { url } on success, a typed body on bad input
|
|
36
|
-
* - No Stripe SDK import in apps/mcp (architectural guard)
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
import { checkout } from "@working-theory/api-client";
|
|
40
|
-
import {
|
|
41
|
-
purchaseInputSchema as PurchaseInputSchema,
|
|
42
|
-
purchaseMcpInputSchema as PurchaseMcpInputSchema,
|
|
43
|
-
purchaseOutputSchema as PurchaseOutputSchema,
|
|
44
|
-
} from "@working-theory/validation";
|
|
45
|
-
|
|
46
|
-
import {
|
|
47
|
-
__setMcpCommerceClaimForTest,
|
|
48
|
-
resetMcpCommerceClaim,
|
|
49
|
-
} from "../../src/lib/commerce-claim.js";
|
|
50
|
-
import { ailkPurchase } from "../../src/tools/purchase.js";
|
|
51
|
-
|
|
52
|
-
jest.mock("@working-theory/api-client", () => ({
|
|
53
|
-
checkout: { createSession: jest.fn() },
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
const createSession = checkout.createSession as jest.MockedFunction<
|
|
57
|
-
typeof checkout.createSession
|
|
58
|
-
>;
|
|
59
|
-
|
|
60
|
-
function sessionCreated(url: string) {
|
|
61
|
-
return { ok: true as const, body: { sessionId: "cs_test_id", url } };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
beforeEach(() => {
|
|
65
|
-
// Every direct `ailkPurchase` call below bypasses registration, so gate 2
|
|
66
|
-
// would answer the module-disabled body under the fail-closed default.
|
|
67
|
-
__setMcpCommerceClaimForTest("claimed");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
afterEach(() => {
|
|
71
|
-
jest.clearAllMocks();
|
|
72
|
-
resetMcpCommerceClaim();
|
|
73
|
-
delete process.env.AILK_BASE_URL;
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// ── Shared input schema (purchaseInputSchema — byte-unchanged, #6001 AC3) ──────
|
|
77
|
-
|
|
78
|
-
describe("PurchaseInputSchema — the shared schema (Server Action / API route)", () => {
|
|
79
|
-
it("accepts valid input with productCode and quantity", () => {
|
|
80
|
-
const result = PurchaseInputSchema.safeParse({
|
|
81
|
-
productCode: "price_test_123",
|
|
82
|
-
quantity: 1,
|
|
83
|
-
});
|
|
84
|
-
expect(result.success).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("accepts valid input with productCode only (quantity defaults)", () => {
|
|
88
|
-
const result = PurchaseInputSchema.safeParse({
|
|
89
|
-
productCode: "price_test_abc",
|
|
90
|
-
});
|
|
91
|
-
expect(result.success).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("accepts valid input with successUrl and cancelUrl", () => {
|
|
95
|
-
const result = PurchaseInputSchema.safeParse({
|
|
96
|
-
productCode: "price_test_123",
|
|
97
|
-
successUrl: "https://example.com/success",
|
|
98
|
-
cancelUrl: "https://example.com/cancel",
|
|
99
|
-
});
|
|
100
|
-
expect(result.success).toBe(true);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("accepts an optional allowPromotionCodes boolean", () => {
|
|
104
|
-
const result = PurchaseInputSchema.safeParse({
|
|
105
|
-
productCode: "price_test_123",
|
|
106
|
-
allowPromotionCodes: true,
|
|
107
|
-
});
|
|
108
|
-
expect(result.success).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("rejects a non-boolean allowPromotionCodes", () => {
|
|
112
|
-
const result = PurchaseInputSchema.safeParse({
|
|
113
|
-
productCode: "price_test_123",
|
|
114
|
-
allowPromotionCodes: "yes",
|
|
115
|
-
});
|
|
116
|
-
expect(result.success).toBe(false);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("rejects input missing productCode", () => {
|
|
120
|
-
const result = PurchaseInputSchema.safeParse({ quantity: 1 });
|
|
121
|
-
expect(result.success).toBe(false);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("rejects input with empty productCode", () => {
|
|
125
|
-
const result = PurchaseInputSchema.safeParse({ productCode: "" });
|
|
126
|
-
expect(result.success).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("rejects a productCode outside the route's character set", () => {
|
|
130
|
-
const result = PurchaseInputSchema.safeParse({
|
|
131
|
-
productCode: "has spaces",
|
|
132
|
-
});
|
|
133
|
-
expect(result.success).toBe(false);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("rejects a productCode longer than 200 characters", () => {
|
|
137
|
-
const result = PurchaseInputSchema.safeParse({
|
|
138
|
-
productCode: "a".repeat(201),
|
|
139
|
-
});
|
|
140
|
-
expect(result.success).toBe(false);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("rejects input with non-integer quantity", () => {
|
|
144
|
-
const result = PurchaseInputSchema.safeParse({
|
|
145
|
-
productCode: "price_x",
|
|
146
|
-
quantity: 1.5,
|
|
147
|
-
});
|
|
148
|
-
expect(result.success).toBe(false);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("rejects input with quantity less than 1", () => {
|
|
152
|
-
const result = PurchaseInputSchema.safeParse({
|
|
153
|
-
productCode: "price_x",
|
|
154
|
-
quantity: 0,
|
|
155
|
-
});
|
|
156
|
-
expect(result.success).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("rejects input with quantity above the route's maximum of 999", () => {
|
|
160
|
-
const result = PurchaseInputSchema.safeParse({
|
|
161
|
-
productCode: "price_x",
|
|
162
|
-
quantity: 1000,
|
|
163
|
-
});
|
|
164
|
-
expect(result.success).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("rejects a non-http(s) redirect URL the route would reject", () => {
|
|
168
|
-
const result = PurchaseInputSchema.safeParse({
|
|
169
|
-
productCode: "price_x",
|
|
170
|
-
successUrl: "javascript:alert(1)",
|
|
171
|
-
});
|
|
172
|
-
expect(result.success).toBe(false);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// ── MCP-only input schema (purchaseMcpInputSchema — the tool's own gate) ───────
|
|
177
|
-
|
|
178
|
-
describe("PurchaseMcpInputSchema — the registered tool's OWN gate (#6001)", () => {
|
|
179
|
-
it("accepts productCode and quantity", () => {
|
|
180
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
181
|
-
productCode: "price_test_123",
|
|
182
|
-
quantity: 2,
|
|
183
|
-
});
|
|
184
|
-
expect(result.success).toBe(true);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("accepts productCode alone (quantity optional)", () => {
|
|
188
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
189
|
-
productCode: "price_test_123",
|
|
190
|
-
});
|
|
191
|
-
expect(result.success).toBe(true);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it("rejects a call carrying successUrl", () => {
|
|
195
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
196
|
-
productCode: "price_test_123",
|
|
197
|
-
successUrl: "https://example.com/success",
|
|
198
|
-
});
|
|
199
|
-
expect(result.success).toBe(false);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("rejects a call carrying cancelUrl", () => {
|
|
203
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
204
|
-
productCode: "price_test_123",
|
|
205
|
-
cancelUrl: "https://example.com/cancel",
|
|
206
|
-
});
|
|
207
|
-
expect(result.success).toBe(false);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it("rejects a call carrying allowPromotionCodes", () => {
|
|
211
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
212
|
-
productCode: "price_test_123",
|
|
213
|
-
allowPromotionCodes: true,
|
|
214
|
-
});
|
|
215
|
-
expect(result.success).toBe(false);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it("rejects a call carrying all three excluded fields at once", () => {
|
|
219
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
220
|
-
productCode: "price_test_123",
|
|
221
|
-
successUrl: "https://attacker.example/phish",
|
|
222
|
-
cancelUrl: "https://attacker.example/cancel",
|
|
223
|
-
allowPromotionCodes: true,
|
|
224
|
-
});
|
|
225
|
-
expect(result.success).toBe(false);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("still rejects missing/invalid productCode, same as the shared schema", () => {
|
|
229
|
-
expect(PurchaseMcpInputSchema.safeParse({}).success).toBe(false);
|
|
230
|
-
expect(
|
|
231
|
-
PurchaseMcpInputSchema.safeParse({ productCode: "" }).success,
|
|
232
|
-
).toBe(false);
|
|
233
|
-
expect(
|
|
234
|
-
PurchaseMcpInputSchema.safeParse({ productCode: "has spaces" }).success,
|
|
235
|
-
).toBe(false);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("still rejects a quantity above the route's maximum of 999", () => {
|
|
239
|
-
const result = PurchaseMcpInputSchema.safeParse({
|
|
240
|
-
productCode: "price_x",
|
|
241
|
-
quantity: 1000,
|
|
242
|
-
});
|
|
243
|
-
expect(result.success).toBe(false);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// ── Output schema (live shape) ────────────────────────────────────────────────
|
|
248
|
-
|
|
249
|
-
describe("PurchaseOutputSchema", () => {
|
|
250
|
-
it("accepts the live success shape { url: string }", () => {
|
|
251
|
-
const result = PurchaseOutputSchema.safeParse({
|
|
252
|
-
url: "https://checkout.stripe.com/pay/cs_test_abc123",
|
|
253
|
-
});
|
|
254
|
-
expect(result.success).toBe(true);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("rejects output missing url", () => {
|
|
258
|
-
const result = PurchaseOutputSchema.safeParse({});
|
|
259
|
-
expect(result.success).toBe(false);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("rejects output where url is not a string", () => {
|
|
263
|
-
const result = PurchaseOutputSchema.safeParse({ url: 42 });
|
|
264
|
-
expect(result.success).toBe(false);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// ── ailkPurchase handler ──────────────────────────────────────────────────────
|
|
269
|
-
|
|
270
|
-
describe("ailkPurchase — valid input", () => {
|
|
271
|
-
it("calls POST /v1/checkout/sessions with the mapped payload", async () => {
|
|
272
|
-
createSession.mockResolvedValueOnce(
|
|
273
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_abc123"),
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
await ailkPurchase({
|
|
277
|
-
productCode: "growth_monthly",
|
|
278
|
-
quantity: 1,
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
expect(createSession).toHaveBeenCalledTimes(1);
|
|
282
|
-
const payload = createSession.mock.calls[0]![0];
|
|
283
|
-
expect(payload.productCode).toBe("growth_monthly");
|
|
284
|
-
expect(payload.quantity).toBe(1);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("never forwards allowPromotionCodes — it is not part of the tool's input", async () => {
|
|
288
|
-
createSession.mockResolvedValueOnce(
|
|
289
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_promo"),
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
await ailkPurchase({ productCode: "growth_monthly" });
|
|
293
|
-
|
|
294
|
-
expect(createSession.mock.calls[0]![0]).not.toHaveProperty(
|
|
295
|
-
"allowPromotionCodes",
|
|
296
|
-
);
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it("returns { url } on success", async () => {
|
|
300
|
-
createSession.mockResolvedValueOnce(
|
|
301
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_xyz"),
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
const output = await ailkPurchase({
|
|
305
|
-
productCode: "growth_monthly",
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
expect(output).toEqual({
|
|
309
|
-
url: "https://checkout.stripe.com/pay/cs_test_xyz",
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("output validates against PurchaseOutputSchema", async () => {
|
|
314
|
-
createSession.mockResolvedValueOnce(
|
|
315
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_valid"),
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
const output = await ailkPurchase({ productCode: "growth_monthly" });
|
|
319
|
-
expect(PurchaseOutputSchema.safeParse(output).success).toBe(true);
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
describe("ailkPurchase — invalid input (typed error body, no side effect)", () => {
|
|
324
|
-
it("returns validation_failed for a missing productCode before any call", async () => {
|
|
325
|
-
const result = await ailkPurchase({ quantity: 1 } as never);
|
|
326
|
-
|
|
327
|
-
expect(result).toMatchObject({ error: "validation_failed" });
|
|
328
|
-
expect(createSession).not.toHaveBeenCalled();
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it("returns validation_failed for an empty productCode before any call", async () => {
|
|
332
|
-
const result = await ailkPurchase({ productCode: "" });
|
|
333
|
-
|
|
334
|
-
expect(result).toMatchObject({ error: "validation_failed" });
|
|
335
|
-
expect(createSession).not.toHaveBeenCalled();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("returns validation_failed for a call carrying successUrl (#6001 AC1)", async () => {
|
|
339
|
-
const result = await ailkPurchase({
|
|
340
|
-
productCode: "price_test_123",
|
|
341
|
-
successUrl: "https://attacker.example/phish",
|
|
342
|
-
} as never);
|
|
343
|
-
|
|
344
|
-
expect(result).toMatchObject({ error: "validation_failed" });
|
|
345
|
-
expect(createSession).not.toHaveBeenCalled();
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("returns validation_failed for a call carrying cancelUrl (#6001 AC1)", async () => {
|
|
349
|
-
const result = await ailkPurchase({
|
|
350
|
-
productCode: "price_test_123",
|
|
351
|
-
cancelUrl: "https://attacker.example/cancel",
|
|
352
|
-
} as never);
|
|
353
|
-
|
|
354
|
-
expect(result).toMatchObject({ error: "validation_failed" });
|
|
355
|
-
expect(createSession).not.toHaveBeenCalled();
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("returns validation_failed for a call carrying allowPromotionCodes (#6001 AC1)", async () => {
|
|
359
|
-
const result = await ailkPurchase({
|
|
360
|
-
productCode: "price_test_123",
|
|
361
|
-
allowPromotionCodes: true,
|
|
362
|
-
} as never);
|
|
363
|
-
|
|
364
|
-
expect(result).toMatchObject({ error: "validation_failed" });
|
|
365
|
-
expect(createSession).not.toHaveBeenCalled();
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
describe("ailkPurchase — always derives redirects from AILK_BASE_URL (#6001 AC2)", () => {
|
|
370
|
-
it("derives both redirect URLs from AILK_BASE_URL when it is set", async () => {
|
|
371
|
-
process.env.AILK_BASE_URL = "https://example.com";
|
|
372
|
-
createSession.mockResolvedValueOnce(
|
|
373
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_base"),
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
await ailkPurchase({ productCode: "growth_monthly" });
|
|
377
|
-
|
|
378
|
-
expect(createSession).toHaveBeenCalledWith({
|
|
379
|
-
productCode: "growth_monthly",
|
|
380
|
-
successUrl:
|
|
381
|
-
"https://example.com/purchase/success?session_id={CHECKOUT_SESSION_ID}",
|
|
382
|
-
cancelUrl: "https://example.com/purchase/cancel",
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
it("falls back to the tool's existing default when AILK_BASE_URL is unset", async () => {
|
|
387
|
-
delete process.env.AILK_BASE_URL;
|
|
388
|
-
delete process.env.NEXT_PUBLIC_BASE_URL;
|
|
389
|
-
createSession.mockResolvedValueOnce(
|
|
390
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_fallback"),
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
await ailkPurchase({ productCode: "growth_monthly" });
|
|
394
|
-
|
|
395
|
-
expect(createSession).toHaveBeenCalledWith({
|
|
396
|
-
productCode: "growth_monthly",
|
|
397
|
-
successUrl:
|
|
398
|
-
"http://localhost:3000/purchase/success?session_id={CHECKOUT_SESSION_ID}",
|
|
399
|
-
cancelUrl: "http://localhost:3000/purchase/cancel",
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
// #6084 AC-6/D1 — the literal template token survives, and cancelUrl
|
|
404
|
-
// carries no session parameter at all.
|
|
405
|
-
it("successUrl carries the literal {CHECKOUT_SESSION_ID} token; cancelUrl carries no session parameter (#6084 D1)", async () => {
|
|
406
|
-
process.env.AILK_BASE_URL = "https://example.com";
|
|
407
|
-
createSession.mockResolvedValueOnce(
|
|
408
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_braces"),
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
await ailkPurchase({ productCode: "growth_monthly" });
|
|
412
|
-
|
|
413
|
-
const payload = createSession.mock.calls[0]![0] as {
|
|
414
|
-
successUrl: string;
|
|
415
|
-
cancelUrl: string;
|
|
416
|
-
};
|
|
417
|
-
expect(payload.successUrl).toContain("{CHECKOUT_SESSION_ID}");
|
|
418
|
-
expect(payload.cancelUrl).not.toContain("session_id");
|
|
419
|
-
expect(payload.cancelUrl).not.toContain("{CHECKOUT_SESSION_ID}");
|
|
420
|
-
});
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
describe("ailkPurchase — parity shape with purchaseAction", () => {
|
|
424
|
-
it("success shape is { url: string } matching the Server Action shape", async () => {
|
|
425
|
-
createSession.mockResolvedValueOnce(
|
|
426
|
-
sessionCreated("https://checkout.stripe.com/pay/cs_test_parity"),
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
const output = await ailkPurchase({
|
|
430
|
-
productCode: "growth_monthly",
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
expect(output).toHaveProperty("url");
|
|
434
|
-
expect(typeof (output as { url: string }).url).toBe("string");
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
// ── Architectural guard ───────────────────────────────────────────────────────
|
|
439
|
-
|
|
440
|
-
describe("architectural guard — no Stripe import", () => {
|
|
441
|
-
it("apps/mcp/src/tools/purchase.ts does not import stripe", async () => {
|
|
442
|
-
const { readFileSync } = await import("node:fs");
|
|
443
|
-
const { resolve } = await import("node:path");
|
|
444
|
-
const src = readFileSync(
|
|
445
|
-
resolve(__dirname, "../../src/tools/purchase.ts"),
|
|
446
|
-
"utf-8",
|
|
447
|
-
);
|
|
448
|
-
expect(src).not.toMatch(/import.*['"]stripe['"]/);
|
|
449
|
-
expect(src).not.toMatch(/require\s*\(\s*['"]stripe['"]/);
|
|
450
|
-
});
|
|
451
|
-
});
|