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,535 +0,0 @@
|
|
|
1
|
-
// Not marked oss: false (D-48/#5366 escalation) — tests the
|
|
2
|
-
// always-shipping project-listings anonymous surface; see
|
|
3
|
-
// routes/project-listings/me.ts's header comment.
|
|
4
|
-
/**
|
|
5
|
-
* @file structured-address.test.ts
|
|
6
|
-
*
|
|
7
|
-
* THE ACCEPTANCE GATE for the structured founder address (#5279).
|
|
8
|
-
*
|
|
9
|
-
* The application collected a whole business address in ONE free-text box.
|
|
10
|
-
* It now renders a fieldset, and the payload field `address` became a UNION:
|
|
11
|
-
* the legacy display STRING or the structured PARTS. The row keeps `address`
|
|
12
|
-
* as the single-line DISPLAY string and gains six nullable part columns
|
|
13
|
-
* beside it, which the server writes — and composes the display line from —
|
|
14
|
-
* in `draftUpdateData`.
|
|
15
|
-
*
|
|
16
|
-
* Two properties carry this change, and both look fine when broken, which is
|
|
17
|
-
* why they each get a test that fails when they are:
|
|
18
|
-
*
|
|
19
|
-
* 1. The parts arrive as PARTS. A patch carrying the structured object must
|
|
20
|
-
* land six DISTINCT column values, not one concatenated string in
|
|
21
|
-
* `address` with the columns left null. A service that quietly stringified
|
|
22
|
-
* the object would produce a row that reads correctly on the listing page
|
|
23
|
-
* and has silently thrown away the split the fieldset exists to capture.
|
|
24
|
-
*
|
|
25
|
-
* 2. A legacy free-text address round-trips CHARACTER-FOR-CHARACTER with its
|
|
26
|
-
* parts null. Nothing may ever split an existing `address` into parts:
|
|
27
|
-
* address parsing guesses, and a wrong guess silently corrupts the party
|
|
28
|
-
* identification the listing's privacy notice points at (#5036 D6), with
|
|
29
|
-
* nothing on the row to mark it as invented. A null part is honest; a
|
|
30
|
-
* guessed one is not.
|
|
31
|
-
*
|
|
32
|
-
* Same harness as `configured-application.test.ts` and `drafts.test.ts`: real
|
|
33
|
-
* routes, real services, mocked `lib/prisma.js` over a small in-memory row
|
|
34
|
-
* store, `routeLead` running for real against a per-test temp path.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
// eslint-disable-next-line no-restricted-syntax -- prisma is a DB boundary; __mocks__/prisma.ts activates the auto-mock
|
|
38
|
-
jest.mock("../../../lib/prisma.js");
|
|
39
|
-
|
|
40
|
-
jest.mock("@working-theory/email", () => ({
|
|
41
|
-
sendLeadNotification: jest.fn().mockResolvedValue({ ok: true, messageId: "msg-test" }),
|
|
42
|
-
// #5406 — `start` now reaches the resume email. Declared here for the reason
|
|
43
|
-
// docs/lessons/project-listings/04 records: a mock factory that omits an
|
|
44
|
-
// export the code under test calls leaves it `undefined` at the callsite.
|
|
45
|
-
// These suites configure no `resumeEmail`, so the send short-circuits before
|
|
46
|
-
// this is reached; it is declared so a future fixture that DOES configure
|
|
47
|
-
// one fails on its assertion rather than on a missing mock.
|
|
48
|
-
sendListingResumeEmail: jest.fn().mockResolvedValue({ ok: true, messageId: "msg-resume" }),
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
jest.mock("@working-theory/license", () => ({
|
|
52
|
-
checkLicense: jest.fn().mockResolvedValue({
|
|
53
|
-
tier: "free",
|
|
54
|
-
plan: null,
|
|
55
|
-
features: [],
|
|
56
|
-
expiresAt: null,
|
|
57
|
-
revoked: false,
|
|
58
|
-
}),
|
|
59
|
-
}));
|
|
60
|
-
|
|
61
|
-
import Fastify from "fastify";
|
|
62
|
-
import type { FastifyInstance } from "fastify";
|
|
63
|
-
|
|
64
|
-
import { setupLeadFileEnv } from "../../../__tests__/factories/lead-fixture";
|
|
65
|
-
import {
|
|
66
|
-
mockNoWorkspace,
|
|
67
|
-
mockWorkspaceExists,
|
|
68
|
-
} from "../../../lib/__mocks__/prisma.js";
|
|
69
|
-
import { prisma } from "../../../lib/prisma.js";
|
|
70
|
-
import { patchProjectListingDraftRoute } from "../patch-draft.js";
|
|
71
|
-
import { startProjectListingRoute } from "../start.js";
|
|
72
|
-
import { submitProjectListingRoute } from "../submit.js";
|
|
73
|
-
|
|
74
|
-
// ─── Fixtures ───────────────────────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
const SITE_ID = "site-1";
|
|
77
|
-
const SITE_KEY = "pk-site-1";
|
|
78
|
-
|
|
79
|
-
const opt = (value: string, label: string) => ({ value, label });
|
|
80
|
-
|
|
81
|
-
const VALID_QUESTIONS = {
|
|
82
|
-
q2: {
|
|
83
|
-
prompt: "Why are you here?",
|
|
84
|
-
options: [opt("scale", "Scaling my team"), opt("just-curious", "Just curious")],
|
|
85
|
-
},
|
|
86
|
-
q3: { prompt: "Stage?", options: [opt("a", "A"), opt("b", "B"), opt("c", "C")] },
|
|
87
|
-
q4: { prompt: "Team size?", options: [opt("a", "A"), opt("b", "B"), opt("c", "C")] },
|
|
88
|
-
q5: {
|
|
89
|
-
prompt: "Urgency?",
|
|
90
|
-
options: [opt("urgent", "Now"), opt("planning", "Soon"), opt("exploring", "Someday")],
|
|
91
|
-
},
|
|
92
|
-
q6: {
|
|
93
|
-
prompt: "Pain?",
|
|
94
|
-
options: [
|
|
95
|
-
opt("low", "Low"),
|
|
96
|
-
opt("medium", "Medium"),
|
|
97
|
-
opt("high", "High"),
|
|
98
|
-
opt("critical", "Critical"),
|
|
99
|
-
],
|
|
100
|
-
},
|
|
101
|
-
q7: { prompt: "Anything else?" },
|
|
102
|
-
extras: [],
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const START_PAYLOAD = {
|
|
106
|
-
siteKey: SITE_KEY,
|
|
107
|
-
founderName: "Ada Founder",
|
|
108
|
-
founderEmail: "ada@example.com",
|
|
109
|
-
consent: true,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
/** Everything the ten steps after step one collect, minus the address. */
|
|
113
|
-
const APPLICATION_WITHOUT_ADDRESS = {
|
|
114
|
-
name: "My Project",
|
|
115
|
-
headline: "Ten words or fewer for the tile title here",
|
|
116
|
-
oneLiner: "A short sentence describing what the project does for its people.",
|
|
117
|
-
problem: "The problem is real and this describes it.",
|
|
118
|
-
product: "wireframes",
|
|
119
|
-
commitment: "part-time",
|
|
120
|
-
teamComposition: "co-founders",
|
|
121
|
-
faqs: [
|
|
122
|
-
{ question: "Q one?", answer: "A one." },
|
|
123
|
-
{ question: "Q two?", answer: "A two." },
|
|
124
|
-
{ question: "Q three?", answer: "A three." },
|
|
125
|
-
],
|
|
126
|
-
questions: VALID_QUESTIONS,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/** What `AddressFieldset` posts — six controls under six fixed keys. */
|
|
130
|
-
const ADDRESS_PARTS = {
|
|
131
|
-
addressLine1: "221B Baker Street",
|
|
132
|
-
addressLine2: "Flat B",
|
|
133
|
-
city: "London",
|
|
134
|
-
region: "Greater London",
|
|
135
|
-
postalCode: "NW1 6XE",
|
|
136
|
-
country: "GB",
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* The display line the SERVER must compose from `ADDRESS_PARTS` — the parts
|
|
141
|
-
* in reading order, joined with ", ". Written out literally rather than by
|
|
142
|
-
* calling `composeAddressLine`, so a change to the joiner fails this test
|
|
143
|
-
* instead of travelling through it.
|
|
144
|
-
*/
|
|
145
|
-
const COMPOSED_ADDRESS_LINE =
|
|
146
|
-
"221B Baker Street, Flat B, London, Greater London, NW1 6XE, GB";
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* An address a founder typed into the ONE free-text box, before #5279. It is
|
|
150
|
-
* not in the composed order, its parts are not separable by any rule, and
|
|
151
|
-
* nothing may ever try: it is here to be returned unchanged.
|
|
152
|
-
*/
|
|
153
|
-
const LEGACY_ADDRESS_LINE =
|
|
154
|
-
"221B Baker Street, Marylebone, London NW1 6XE, United Kingdom";
|
|
155
|
-
|
|
156
|
-
// ─── The in-memory row store the Prisma mock is backed by ───────────────────
|
|
157
|
-
|
|
158
|
-
type Row = Record<string, unknown>;
|
|
159
|
-
|
|
160
|
-
let rows: Row[];
|
|
161
|
-
let nextId: number;
|
|
162
|
-
|
|
163
|
-
function matches(row: Row, where: Record<string, unknown>): boolean {
|
|
164
|
-
return Object.entries(where).every(([key, value]) => {
|
|
165
|
-
if (key === "OR") {
|
|
166
|
-
return (value as Record<string, unknown>[]).some((clause) => matches(row, clause));
|
|
167
|
-
}
|
|
168
|
-
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
169
|
-
const clause = value as Record<string, unknown>;
|
|
170
|
-
if ("in" in clause) return (clause["in"] as unknown[]).includes(row[key]);
|
|
171
|
-
if ("not" in clause) return row[key] !== clause["not"];
|
|
172
|
-
if ("gt" in clause) return (row[key] as Date) > (clause["gt"] as Date);
|
|
173
|
-
if ("startsWith" in clause) {
|
|
174
|
-
return (
|
|
175
|
-
typeof row[key] === "string" &&
|
|
176
|
-
(row[key] as string).startsWith(clause["startsWith"] as string)
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return row[key] === value;
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function installStore(): void {
|
|
185
|
-
// #6335 — the shared prisma mock no longer resolves ANY requested
|
|
186
|
-
// workspace id. `startProjectListingDraft` stamps the row's `workspaceId`
|
|
187
|
-
// by resolving the site it was filed against (`resolveWorkspaceIdForSite`,
|
|
188
|
-
// services/project-listings.ts), so this suite states the ONE site it is
|
|
189
|
-
// built around. Keyed on the requested id — nothing else resolves.
|
|
190
|
-
mockWorkspaceExists(prisma, SITE_ID);
|
|
191
|
-
|
|
192
|
-
rows = [];
|
|
193
|
-
nextId = 1;
|
|
194
|
-
|
|
195
|
-
(prisma.lead.create as jest.Mock).mockImplementation(() =>
|
|
196
|
-
Promise.resolve({ id: `lead_00000000-0000-7000-8000-00000000000${nextId}` }),
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
(prisma.siteListingConfig.findUnique as jest.Mock).mockImplementation(
|
|
200
|
-
({ where }: { where: { siteId?: string; publicKey?: string } }) => {
|
|
201
|
-
if (where.publicKey !== undefined) {
|
|
202
|
-
return Promise.resolve(where.publicKey === SITE_KEY ? { siteId: SITE_ID } : null);
|
|
203
|
-
}
|
|
204
|
-
// No config row — the platform defaults, which collect `address`.
|
|
205
|
-
return Promise.resolve(null);
|
|
206
|
-
},
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
// #5275 — the grant write `start` makes after the row. Append-only; it only
|
|
210
|
-
// needs to resolve here (drafts.test.ts asserts on what it is called with).
|
|
211
|
-
(prisma.consentGrant.createMany as jest.Mock).mockImplementation(
|
|
212
|
-
({ data }: { data: Row[] }) => Promise.resolve({ count: data.length }),
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// #5275 — `start` writes the row and its grant in one interactive
|
|
216
|
-
// transaction; the mock hands the same store-backed client through.
|
|
217
|
-
(prisma.$transaction as jest.Mock).mockImplementation(
|
|
218
|
-
async (fn: (tx: typeof prisma) => Promise<unknown>) => fn(prisma),
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
(prisma.projectListing.create as jest.Mock).mockImplementation(({ data }: { data: Row }) => {
|
|
222
|
-
const now = new Date();
|
|
223
|
-
const row: Row = {
|
|
224
|
-
id: `prjl_00000000-0000-7000-8000-00000000000${nextId++}`,
|
|
225
|
-
slug: null,
|
|
226
|
-
name: null,
|
|
227
|
-
oneLiner: null,
|
|
228
|
-
problem: null,
|
|
229
|
-
url: null,
|
|
230
|
-
product: null,
|
|
231
|
-
commitment: null,
|
|
232
|
-
teamComposition: null,
|
|
233
|
-
faqs: null,
|
|
234
|
-
anythingElse: null,
|
|
235
|
-
address: null,
|
|
236
|
-
// #5279 — a freshly-started draft has no parts, exactly like every row
|
|
237
|
-
// written before the columns existed.
|
|
238
|
-
addressLine1: null,
|
|
239
|
-
addressLine2: null,
|
|
240
|
-
addressCity: null,
|
|
241
|
-
addressRegion: null,
|
|
242
|
-
addressPostalCode: null,
|
|
243
|
-
addressCountry: null,
|
|
244
|
-
questions: null,
|
|
245
|
-
// #5407 — a row written before the columns existed, and a freshly
|
|
246
|
-
// started draft on a site that asks for one name line: parts null,
|
|
247
|
-
// no phone, no verification stamp.
|
|
248
|
-
founderFirstName: null,
|
|
249
|
-
founderLastName: null,
|
|
250
|
-
founderPhone: null,
|
|
251
|
-
emailVerifiedAt: null,
|
|
252
|
-
founderCompany: null,
|
|
253
|
-
leadId: null,
|
|
254
|
-
headline: null,
|
|
255
|
-
logoMarkUrl: null,
|
|
256
|
-
logoWordmarkUrl: null,
|
|
257
|
-
solution: null,
|
|
258
|
-
featured: false,
|
|
259
|
-
decidedAt: null,
|
|
260
|
-
decidedBy: null,
|
|
261
|
-
issueUrl: null,
|
|
262
|
-
experimentKey: null,
|
|
263
|
-
windowEndsAt: null,
|
|
264
|
-
createdAt: now,
|
|
265
|
-
updatedAt: now,
|
|
266
|
-
...data,
|
|
267
|
-
};
|
|
268
|
-
rows.push(row);
|
|
269
|
-
return Promise.resolve(row);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
(prisma.projectListing.findFirst as jest.Mock).mockImplementation(
|
|
273
|
-
({ where }: { where: Record<string, unknown> }) =>
|
|
274
|
-
Promise.resolve(rows.find((row) => matches(row, where)) ?? null),
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
(prisma.projectListing.findMany as jest.Mock).mockImplementation(
|
|
278
|
-
({ where }: { where: Record<string, unknown> }) =>
|
|
279
|
-
Promise.resolve(rows.filter((row) => matches(row, where))),
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
(prisma.projectListing.update as jest.Mock).mockImplementation(
|
|
283
|
-
({ where, data }: { where: Record<string, unknown>; data: Row }) => {
|
|
284
|
-
const row = rows.find((r) => matches(r, where));
|
|
285
|
-
Object.assign(row!, data, { updatedAt: new Date() });
|
|
286
|
-
return Promise.resolve(row);
|
|
287
|
-
},
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
(prisma.projectListing.updateMany as jest.Mock).mockImplementation(
|
|
291
|
-
({ where, data }: { where: Record<string, unknown>; data: Row }) => {
|
|
292
|
-
const matched = rows.filter((r) => matches(r, where));
|
|
293
|
-
matched.forEach((r) => Object.assign(r, data, { updatedAt: new Date() }));
|
|
294
|
-
return Promise.resolve({ count: matched.length });
|
|
295
|
-
},
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
300
|
-
|
|
301
|
-
const bearer = (token: string) => ({ authorization: `Bearer ${token}` });
|
|
302
|
-
|
|
303
|
-
async function buildApp(): Promise<FastifyInstance> {
|
|
304
|
-
const app = Fastify();
|
|
305
|
-
await app.register(startProjectListingRoute);
|
|
306
|
-
await app.register(patchProjectListingDraftRoute);
|
|
307
|
-
await app.register(submitProjectListingRoute);
|
|
308
|
-
await app.ready();
|
|
309
|
-
return app;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async function startDraft(
|
|
313
|
-
app: FastifyInstance,
|
|
314
|
-
): Promise<{ id: string; resumeToken: string }> {
|
|
315
|
-
const res = await app.inject({
|
|
316
|
-
method: "POST",
|
|
317
|
-
url: "/v1/project-listings",
|
|
318
|
-
payload: START_PAYLOAD,
|
|
319
|
-
});
|
|
320
|
-
expect(res.statusCode).toBe(201);
|
|
321
|
-
return (res.json() as { data: { id: string; resumeToken: string } }).data;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const patch = (
|
|
325
|
-
app: FastifyInstance,
|
|
326
|
-
draft: { id: string; resumeToken: string },
|
|
327
|
-
payload: Record<string, unknown>,
|
|
328
|
-
) =>
|
|
329
|
-
app.inject({
|
|
330
|
-
method: "PATCH",
|
|
331
|
-
url: `/v1/project-listings/drafts/${draft.id}`,
|
|
332
|
-
headers: bearer(draft.resumeToken),
|
|
333
|
-
payload,
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
const submit = (app: FastifyInstance, draft: { id: string; resumeToken: string }) =>
|
|
337
|
-
app.inject({
|
|
338
|
-
method: "POST",
|
|
339
|
-
url: `/v1/project-listings/drafts/${draft.id}/submit`,
|
|
340
|
-
headers: bearer(draft.resumeToken),
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
const storedRow = (id: string): Row => {
|
|
344
|
-
const row = rows.find((r) => r["id"] === id);
|
|
345
|
-
expect(row).toBeDefined();
|
|
346
|
-
return row!;
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
const draftBody = (res: { json: () => unknown }) =>
|
|
350
|
-
(res.json() as { data: Record<string, unknown> }).data;
|
|
351
|
-
|
|
352
|
-
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
353
|
-
|
|
354
|
-
describe("the structured founder address (#5279)", () => {
|
|
355
|
-
setupLeadFileEnv("project-listing-structured-address");
|
|
356
|
-
let app: FastifyInstance;
|
|
357
|
-
|
|
358
|
-
beforeEach(async () => {
|
|
359
|
-
jest.clearAllMocks();
|
|
360
|
-
installStore();
|
|
361
|
-
app = await buildApp();
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
afterEach(async () => {
|
|
365
|
-
await app.close();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
describe("the parts arrive as PARTS, and the server composes the display line", () => {
|
|
369
|
-
it("stores six DISTINCT column values, not one concatenated string", async () => {
|
|
370
|
-
const draft = await startDraft(app);
|
|
371
|
-
|
|
372
|
-
const res = await patch(app, draft, { address: ADDRESS_PARTS });
|
|
373
|
-
|
|
374
|
-
expect(res.statusCode).toBe(200);
|
|
375
|
-
|
|
376
|
-
// The column-level half. Each part is in its OWN column — a service that
|
|
377
|
-
// stringified the object would leave every one of these null and this
|
|
378
|
-
// test is the thing that notices.
|
|
379
|
-
const row = storedRow(draft.id);
|
|
380
|
-
expect(row["addressLine1"]).toBe("221B Baker Street");
|
|
381
|
-
expect(row["addressLine2"]).toBe("Flat B");
|
|
382
|
-
expect(row["addressCity"]).toBe("London");
|
|
383
|
-
expect(row["addressRegion"]).toBe("Greater London");
|
|
384
|
-
expect(row["addressPostalCode"]).toBe("NW1 6XE");
|
|
385
|
-
expect(row["addressCountry"]).toBe("GB");
|
|
386
|
-
|
|
387
|
-
// The display half. `address` is DERIVED here and only here — the parts
|
|
388
|
-
// in reading order, joined with ", ".
|
|
389
|
-
expect(row["address"]).toBe(COMPOSED_ADDRESS_LINE);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it("answers the save with the composed line AND the parts, separably", async () => {
|
|
393
|
-
const draft = await startDraft(app);
|
|
394
|
-
|
|
395
|
-
const body = draftBody(await patch(app, draft, { address: ADDRESS_PARTS }));
|
|
396
|
-
|
|
397
|
-
expect(body["address"]).toBe(COMPOSED_ADDRESS_LINE);
|
|
398
|
-
expect(body["addressParts"]).toEqual(ADDRESS_PARTS);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it("omits the blank addressLine2 from both the columns and the composed line", async () => {
|
|
402
|
-
const draft = await startDraft(app);
|
|
403
|
-
const { addressLine2: _line2, ...withoutLine2 } = ADDRESS_PARTS;
|
|
404
|
-
|
|
405
|
-
const body = draftBody(await patch(app, draft, { address: withoutLine2 }));
|
|
406
|
-
|
|
407
|
-
expect(storedRow(draft.id)["addressLine2"]).toBeNull();
|
|
408
|
-
expect(body["address"]).toBe("221B Baker Street, London, Greater London, NW1 6XE, GB");
|
|
409
|
-
expect(body["addressParts"]).toEqual(withoutLine2);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it("a composed address submits, and reaches the row as the display line", async () => {
|
|
413
|
-
const draft = await startDraft(app);
|
|
414
|
-
await patch(app, draft, { ...APPLICATION_WITHOUT_ADDRESS, address: ADDRESS_PARTS });
|
|
415
|
-
|
|
416
|
-
const res = await submit(app, draft);
|
|
417
|
-
|
|
418
|
-
expect(res.statusCode).toBe(200);
|
|
419
|
-
expect(storedRow(draft.id)["address"]).toBe(COMPOSED_ADDRESS_LINE);
|
|
420
|
-
expect(storedRow(draft.id)["addressCountry"]).toBe("GB");
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("refuses parts that are not a whole address, rather than storing half of one", async () => {
|
|
424
|
-
const draft = await startDraft(app);
|
|
425
|
-
const { city: _city, ...incomplete } = ADDRESS_PARTS;
|
|
426
|
-
|
|
427
|
-
const res = await patch(app, draft, { address: incomplete });
|
|
428
|
-
|
|
429
|
-
expect(res.statusCode).toBe(400);
|
|
430
|
-
expect(storedRow(draft.id)["addressLine1"]).toBeNull();
|
|
431
|
-
expect(storedRow(draft.id)["address"]).toBeNull();
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
describe("a legacy free-text address round-trips unchanged, with its parts null", () => {
|
|
436
|
-
// The load-bearing guard. Nothing may split an existing `address` into
|
|
437
|
-
// parts — a wrong split silently corrupts the party identification the
|
|
438
|
-
// listing's privacy notice points at, and a null part is honest where a
|
|
439
|
-
// guessed one is not.
|
|
440
|
-
it("comes back CHARACTER-FOR-CHARACTER from a save, with addressParts null", async () => {
|
|
441
|
-
const draft = await startDraft(app);
|
|
442
|
-
|
|
443
|
-
const body = draftBody(await patch(app, draft, { address: LEGACY_ADDRESS_LINE }));
|
|
444
|
-
|
|
445
|
-
expect(body["address"]).toBe(LEGACY_ADDRESS_LINE);
|
|
446
|
-
expect(body["addressParts"]).toBeNull();
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it("leaves EVERY part column untouched — a plain string writes none of them", async () => {
|
|
450
|
-
const draft = await startDraft(app);
|
|
451
|
-
|
|
452
|
-
await patch(app, draft, { address: LEGACY_ADDRESS_LINE });
|
|
453
|
-
|
|
454
|
-
const row = storedRow(draft.id);
|
|
455
|
-
expect(row["address"]).toBe(LEGACY_ADDRESS_LINE);
|
|
456
|
-
for (const column of [
|
|
457
|
-
"addressLine1",
|
|
458
|
-
"addressLine2",
|
|
459
|
-
"addressCity",
|
|
460
|
-
"addressRegion",
|
|
461
|
-
"addressPostalCode",
|
|
462
|
-
"addressCountry",
|
|
463
|
-
]) {
|
|
464
|
-
expect(row[column]).toBeNull();
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it("submits exactly as it did before #5279, and the stored line is unchanged", async () => {
|
|
469
|
-
const draft = await startDraft(app);
|
|
470
|
-
await patch(app, draft, {
|
|
471
|
-
...APPLICATION_WITHOUT_ADDRESS,
|
|
472
|
-
address: LEGACY_ADDRESS_LINE,
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
const res = await submit(app, draft);
|
|
476
|
-
|
|
477
|
-
expect(res.statusCode).toBe(200);
|
|
478
|
-
expect(storedRow(draft.id)["address"]).toBe(LEGACY_ADDRESS_LINE);
|
|
479
|
-
expect(storedRow(draft.id)["addressLine1"]).toBeNull();
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
// A row written before #5279 holds a line and six nulls. Overwriting the
|
|
483
|
-
// line with a NEW line must not start inventing parts for it either.
|
|
484
|
-
it("re-patching a legacy line with another legacy line still writes no parts", async () => {
|
|
485
|
-
const draft = await startDraft(app);
|
|
486
|
-
await patch(app, draft, { address: LEGACY_ADDRESS_LINE });
|
|
487
|
-
|
|
488
|
-
await patch(app, draft, { address: "1 Example Street, Exampleton, EX1 2AM" });
|
|
489
|
-
|
|
490
|
-
const row = storedRow(draft.id);
|
|
491
|
-
expect(row["address"]).toBe("1 Example Street, Exampleton, EX1 2AM");
|
|
492
|
-
expect(row["addressCity"]).toBeNull();
|
|
493
|
-
expect(row["addressCountry"]).toBeNull();
|
|
494
|
-
});
|
|
495
|
-
});
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
// ── #6335 — the explicit workspace setup above is load-bearing ────────────
|
|
499
|
-
//
|
|
500
|
-
// Every case in this file states that this suite's site has a workspace.
|
|
501
|
-
// This one takes that away, and proves the statement was doing work rather
|
|
502
|
-
// than decorating the setup: `startProjectListingDraft` resolves the site it
|
|
503
|
-
// is filing against (`resolveWorkspaceIdForSite`, services/project-listings.ts)
|
|
504
|
-
// and refuses BEFORE any row is written. With the shared prisma mock's old
|
|
505
|
-
// permissive default in place this case could not be written at all — every
|
|
506
|
-
// id resolved, so the fail-closed path was unreachable from a route test.
|
|
507
|
-
describe("#6335 — start fails closed when the site names no workspace", () => {
|
|
508
|
-
let app: FastifyInstance;
|
|
509
|
-
|
|
510
|
-
beforeEach(async () => {
|
|
511
|
-
jest.clearAllMocks();
|
|
512
|
-
installStore();
|
|
513
|
-
mockNoWorkspace(prisma);
|
|
514
|
-
app = await buildApp();
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
afterEach(async () => {
|
|
518
|
-
await app.close();
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
it("answers 503 tenant_unresolved and creates no ProjectListing", async () => {
|
|
522
|
-
const res = await app.inject({
|
|
523
|
-
method: "POST",
|
|
524
|
-
url: "/v1/project-listings",
|
|
525
|
-
payload: START_PAYLOAD,
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
expect(res.statusCode).toBe(503);
|
|
529
|
-
expect((res.json() as { error: { code: string } }).error.code).toBe(
|
|
530
|
-
"tenant_unresolved",
|
|
531
|
-
);
|
|
532
|
-
expect(prisma.projectListing.create).not.toHaveBeenCalled();
|
|
533
|
-
expect(rows).toHaveLength(0);
|
|
534
|
-
});
|
|
535
|
-
});
|