create-ailk 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -22
- package/component-catalog.json +3 -3
- package/component-catalog.md +3 -3
- package/dist/add-module.js +4 -2
- package/dist/add-route.d.ts +12 -1
- package/dist/add-route.js +31 -3
- package/dist/cli.js +12 -7
- package/dist/derive-architecture.d.ts +3 -4
- package/dist/derive-architecture.js +65 -29
- 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 +2 -1
- package/dist/lib/fetch-template-snapshot.js +8 -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 +36 -15
- package/dist/lib/module-tier.d.ts +2 -8
- package/dist/lib/module-tier.js +13 -12
- package/dist/module-architecture.d.ts +20 -3
- package/dist/module-architecture.js +129 -552
- package/dist/paid-sections.d.ts +25 -1
- package/dist/paid-sections.js +88 -3
- package/dist/starter-grid.d.ts +6 -6
- package/dist/starter-grid.js +12 -5
- package/dist/surfaces.js +39 -2
- package/dist/template-record.d.ts +21 -3
- package/dist/template-record.js +38 -7
- package/dist/topology.d.ts +16 -3
- package/dist/topology.js +54 -23
- package/dist/update.d.ts +10 -1
- package/dist/update.js +715 -21
- package/package.json +3 -3
- package/release-scope.json +2 -2
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +8 -2
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +2 -2
- package/site-templates/marketing-site/ailk-template.json +7 -1
- package/site-templates/marketing-site/provenance.json +7 -1
- package/site-templates/saas-product/ailk-template.json +2 -1
- package/site-templates/saas-product/provenance.json +1 -0
- 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 +20 -5
- package/templates/.github/workflows/ci.yml +7 -4
- package/templates/CLAUDE.md +11 -1
- package/templates/CONVENTIONS.md +8 -10
- package/templates/README.md +14 -13
- package/templates/apps/api/.env.example +23 -2
- package/templates/apps/api/CLAUDE.md +119 -83
- 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-client.js +16 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -3
- 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 -1882
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/CLAUDE.md +90 -26
- 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 -13
- 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 +31 -3
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +43 -4
- package/templates/apps/web/app/[locale]/(auth)/legal-footnote.tsx +35 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +117 -8
- package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +14 -37
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +6 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +20 -35
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/page.test.tsx +108 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +111 -5
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +78 -16
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +132 -12
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +53 -1
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +4 -2
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +47 -24
- package/templates/apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx +23 -4
- package/templates/apps/web/app/[locale]/contact/intake-analytics.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 +7 -3
- package/templates/apps/web/app/[locale]/layout.tsx +33 -13
- package/templates/apps/web/app/[locale]/meet/confirmed/BookingHandOff.tsx +128 -0
- package/templates/apps/web/app/[locale]/meet/confirmed/__tests__/BookingHandOff.test.tsx +126 -0
- 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 +10 -6
- package/templates/apps/web/i18n/request.ts +49 -1
- package/templates/apps/web/jest.config.cjs +1 -0
- package/templates/apps/web/lib/__tests__/account-config.test.ts +7 -0
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +276 -0
- package/templates/apps/web/lib/auth-email.ts +103 -21
- package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +72 -23
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +94 -70
- package/templates/apps/web/lib/prisma-for-auth.ts +5 -2
- 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/lib/sign-up-agreements.ts +291 -28
- package/templates/apps/web/lib/site-contributions.ts +18 -0
- package/templates/apps/web/messages/en.json +186 -20
- package/templates/apps/web/messages/site/en.json +1 -0
- 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 +5 -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 +553 -7
- package/templates/tsconfig.json +1 -2
- package/versions.json +49 -36
- 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 -308
- 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 -257
- 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 -321
- 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 -391
- 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 -2148
- 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 -332
- 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 -1426
- 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 -183
- package/templates/apps/api/src/routes/content/index.ts +0 -49
- package/templates/apps/api/src/routes/content/list-pages.ts +0 -74
- 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 -100
- package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
- 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 -639
- 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 -491
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -476
- 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 -449
- 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 -809
- 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 -2028
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -645
- 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 -877
- 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 -1204
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -450
- package/templates/apps/api/src/services/__tests__/project-listings.counts.test.ts +0 -202
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -151
- 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 -336
- 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 -750
- 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 -348
- 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 -975
- 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 -400
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -685
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
- package/templates/apps/api/src/services/offerings/catalog.ts +0 -318
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -403
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
- package/templates/apps/api/src/services/offerings/connection.ts +0 -1083
- 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 -1211
- 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 -2085
- 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 -1232
- 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 -70
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -162
- 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 -180
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
- package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -268
- package/templates/apps/mcp/__tests__/locale.test.ts +0 -120
- 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__/rate-limit-write.test.ts +0 -189
- package/templates/apps/mcp/__tests__/read_content.test.ts +0 -511
- package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -160
- package/templates/apps/mcp/__tests__/search_content.test.ts +0 -271
- 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 -222
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -152
- 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 -349
- 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 -564
- 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 -116
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -140
- 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/site-config.ts +0 -53
- 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 -66
- 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 -138
- package/templates/apps/mcp/src/tools/list_pages.ts +0 -68
- 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 -81
- package/templates/apps/mcp/src/tools/request_callback.ts +0 -52
- package/templates/apps/mcp/src/tools/search_content.ts +0 -63
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -54
- package/templates/apps/mcp/src/workspace-server.ts +0 -224
- package/templates/database/CHANGELOG.md +0 -981
- 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 -272
- package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
- package/templates/database/__tests__/tenancy.test.ts +0 -141
- package/templates/database/__tests__/trial-magnet-migration.test.ts +0 -186
- 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/20261025120000_trial_magnet/migration.sql +0 -28
- package/templates/database/migrations/20261026120000_trial_magnet_proof_line/migration.sql +0 -23
- 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,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file flow-completion-migration.test.ts
|
|
3
|
-
*
|
|
4
|
-
* DB-free proof of the flow_completion migration's shape (#8757, spec
|
|
5
|
-
* docs/specs/8757-a-per-magnet-completion-setting-spec.md D2, AC-2). Reads
|
|
6
|
-
* the migration's own text: it adds exactly the two columns and three named
|
|
7
|
-
* CHECKs, each added `NOT VALID` then `VALIDATE`d, and its one `UPDATE` is the
|
|
8
|
-
* slug backfill between its markers — naming exactly the standard magnets
|
|
9
|
-
* whose default is `invite` (`STANDARD_MAGNET_COMPLETIONS`). It inserts and
|
|
10
|
-
* deletes nothing. The live-database half is `flow-completion.db.test.ts`.
|
|
11
|
-
*/
|
|
12
|
-
import { readFileSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
|
|
15
|
-
import { STANDARD_MAGNET_COMPLETIONS } from "@working-theory/validation";
|
|
16
|
-
|
|
17
|
-
const MIGRATION = join(
|
|
18
|
-
__dirname,
|
|
19
|
-
"..",
|
|
20
|
-
"migrations",
|
|
21
|
-
"20261018120000_flow_completion",
|
|
22
|
-
"migration.sql",
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const CHECKS = [
|
|
26
|
-
"flow_definitions_completion_domain",
|
|
27
|
-
"flow_definitions_checkout_offering_pair",
|
|
28
|
-
"flow_definitions_checkout_offering_format",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
/** The migration's statements, comments stripped, whitespace collapsed. */
|
|
32
|
-
function statements(): string[] {
|
|
33
|
-
const text = readFileSync(MIGRATION, "utf8")
|
|
34
|
-
.split("\n")
|
|
35
|
-
.filter((line) => !line.trimStart().startsWith("--"))
|
|
36
|
-
.join("\n");
|
|
37
|
-
return text
|
|
38
|
-
.split(";")
|
|
39
|
-
.map((s) => s.replace(/\s+/g, " ").trim())
|
|
40
|
-
.filter((s) => s.length > 0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** The text between the `-- BEGIN completion backfill` / `-- END …` markers. */
|
|
44
|
-
function backfillBlock(): string {
|
|
45
|
-
const match = /-- BEGIN completion backfill\n([\s\S]*?)-- END completion backfill/.exec(
|
|
46
|
-
readFileSync(MIGRATION, "utf8"),
|
|
47
|
-
);
|
|
48
|
-
if (!match) throw new Error("completion backfill markers not found");
|
|
49
|
-
return match[1]!;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
describe("the flow_completion migration (#8757 D2, AC-2)", () => {
|
|
53
|
-
it("adds exactly the two columns, the completion default lead", () => {
|
|
54
|
-
const adds = statements().filter((s) => /^ALTER TABLE "flow_definitions" ADD COLUMN /.test(s));
|
|
55
|
-
expect(adds).toHaveLength(1);
|
|
56
|
-
const columns = adds[0]!.split(/ADD COLUMN /).slice(1).map((c) => c.trim().replace(/,$/, ""));
|
|
57
|
-
expect(columns).toEqual([
|
|
58
|
-
`"completion" VARCHAR(16) NOT NULL DEFAULT 'lead'`,
|
|
59
|
-
`"checkout_offering" VARCHAR(40)`,
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("adds the three named CHECKs NOT VALID, then VALIDATEs each", () => {
|
|
64
|
-
const all = statements();
|
|
65
|
-
const added = all
|
|
66
|
-
.map((s) => /^ALTER TABLE "flow_definitions" ADD CONSTRAINT "([a-z_]+)" CHECK \(.*\) NOT VALID$/.exec(s)?.[1])
|
|
67
|
-
.filter((name): name is string => name !== undefined);
|
|
68
|
-
const validated = all
|
|
69
|
-
.map((s) => /^ALTER TABLE "flow_definitions" VALIDATE CONSTRAINT "([a-z_]+)"$/.exec(s)?.[1])
|
|
70
|
-
.filter((name): name is string => name !== undefined);
|
|
71
|
-
expect(added).toEqual(CHECKS);
|
|
72
|
-
expect(validated).toEqual(CHECKS);
|
|
73
|
-
for (const name of CHECKS) {
|
|
74
|
-
expect(all.findIndex((s) => s.includes(`VALIDATE CONSTRAINT "${name}"`))).toBeGreaterThan(
|
|
75
|
-
all.findIndex((s) => s.includes(`ADD CONSTRAINT "${name}"`)),
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("holds exactly one UPDATE, inside the backfill markers, naming the invite defaults", () => {
|
|
81
|
-
const updates = statements().filter((s) => /\bUPDATE\b/i.test(s));
|
|
82
|
-
expect(updates).toHaveLength(1);
|
|
83
|
-
const block = backfillBlock().replace(/\s+/g, " ").trim();
|
|
84
|
-
expect(block).toBe(updates[0] + ";");
|
|
85
|
-
const list = /WHERE "slug" IN \(([^)]*)\)/.exec(block)?.[1];
|
|
86
|
-
const slugs = (list ?? "").split(",").map((s) => s.trim().replace(/^'|'$/g, ""));
|
|
87
|
-
const invite = Object.entries(STANDARD_MAGNET_COMPLETIONS)
|
|
88
|
-
.filter(([, completion]) => completion === "invite")
|
|
89
|
-
.map(([slug]) => slug);
|
|
90
|
-
expect([...slugs].sort()).toEqual([...invite].sort());
|
|
91
|
-
expect(block).toContain(`SET "completion" = 'invite'`);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("inserts and deletes nothing", () => {
|
|
95
|
-
for (const s of statements()) expect(s).not.toMatch(/\b(INSERT|DELETE)\b/i);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("every statement is one of the expected shapes", () => {
|
|
99
|
-
const unexpected = statements().filter(
|
|
100
|
-
(s) =>
|
|
101
|
-
!/^ALTER TABLE "flow_definitions" (ADD COLUMN|ADD CONSTRAINT|VALIDATE CONSTRAINT) /.test(s) &&
|
|
102
|
-
!/^UPDATE "flow_definitions" SET "completion" = 'invite' WHERE "slug" IN \(/.test(s),
|
|
103
|
-
);
|
|
104
|
-
expect(unexpected).toEqual([]);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("schema.prisma declares both fields on FlowDefinition", () => {
|
|
108
|
-
const prisma = readFileSync(join(__dirname, "..", "inbox", "schema.prisma"), "utf8");
|
|
109
|
-
const model = /model FlowDefinition \{([\s\S]*?)\n\}/.exec(prisma)?.[1] ?? "";
|
|
110
|
-
expect(model).toMatch(/\n\s*completion String @default\("lead"\) @db\.VarChar\(16\)\n/);
|
|
111
|
-
expect(model).toMatch(
|
|
112
|
-
/\n\s*checkoutOffering String\? @map\("checkout_offering"\) @db\.VarChar\(40\)\n/,
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file flow-completion.db.test.ts
|
|
3
|
-
*
|
|
4
|
-
* LIVE-DATABASE proof of a magnet's completion columns (#8757, spec
|
|
5
|
-
* docs/specs/8757-a-per-magnet-completion-setting-spec.md D2, AC-2). Requires
|
|
6
|
-
* DATABASE_URL pointing at a Postgres with the migrations applied — SKIPS
|
|
7
|
-
* when DATABASE_URL is unset (the repo convention; the `apps/api Route Tests
|
|
8
|
-
* (Postgres)` workflow exercises it).
|
|
9
|
-
*
|
|
10
|
-
* Uses the `pg` driver directly, in ONE transaction that is rolled back. What
|
|
11
|
-
* it proves that a mock cannot:
|
|
12
|
-
* 1. The migration's backfill block, run over magnets at the five slugs,
|
|
13
|
-
* leaves `contact` and `form` at `lead` and sets the three process
|
|
14
|
-
* magnets to `invite`; a second run changes nothing.
|
|
15
|
-
* 2. Each CHECK refuses what it names.
|
|
16
|
-
*/
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
18
|
-
import { join } from "node:path";
|
|
19
|
-
|
|
20
|
-
import { Client } from "pg";
|
|
21
|
-
|
|
22
|
-
const describeDb = process.env.DATABASE_URL ? describe : describe.skip;
|
|
23
|
-
|
|
24
|
-
const MIGRATION_SQL = readFileSync(
|
|
25
|
-
join(__dirname, "..", "migrations", "20261018120000_flow_completion", "migration.sql"),
|
|
26
|
-
"utf8",
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
/** The text between the `-- BEGIN completion backfill` / `-- END …` markers. */
|
|
30
|
-
function backfillBlock(sql: string): string {
|
|
31
|
-
const match = /-- BEGIN completion backfill\n([\s\S]*?)-- END completion backfill/.exec(sql);
|
|
32
|
-
if (!match?.[1]) throw new Error("completion backfill markers not found");
|
|
33
|
-
return match[1];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const WS = "wksp_01920000-8757-7000-8000-0000000000c1";
|
|
37
|
-
const SLUGS = ["contact", "onboarding", "request-quote", "job-application", "form"] as const;
|
|
38
|
-
|
|
39
|
-
describeDb("the magnet completion columns (#8757 D2, AC-2, live DB)", () => {
|
|
40
|
-
let db: Client;
|
|
41
|
-
|
|
42
|
-
beforeAll(async () => {
|
|
43
|
-
db = new Client({ connectionString: process.env.DATABASE_URL });
|
|
44
|
-
await db.connect();
|
|
45
|
-
await db.query("BEGIN");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
afterAll(async () => {
|
|
49
|
-
await db.query("ROLLBACK");
|
|
50
|
-
await db.end();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
/** Insert one magnet at `slug`, with any of the completion columns. */
|
|
54
|
-
const insertMagnet = (slug: string, cols: Record<string, unknown> = {}) => {
|
|
55
|
-
const all: Record<string, unknown> = {
|
|
56
|
-
site_id: WS,
|
|
57
|
-
workspace_id: WS,
|
|
58
|
-
slug,
|
|
59
|
-
config: JSON.stringify({ steps: [] }),
|
|
60
|
-
title: `Magnet ${slug}`,
|
|
61
|
-
status: "draft",
|
|
62
|
-
updated_at: new Date("2026-10-01T09:00:00.000Z"),
|
|
63
|
-
...cols,
|
|
64
|
-
};
|
|
65
|
-
const names = Object.keys(all);
|
|
66
|
-
return db.query<{ id: string }>(
|
|
67
|
-
`INSERT INTO "flow_definitions" (${names.map((n) => `"${n}"`).join(", ")})
|
|
68
|
-
VALUES (${names.map((_, i) => `$${i + 1}`).join(", ")}) RETURNING "id"`,
|
|
69
|
-
names.map((n) => all[n]),
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/** Run `run` inside a savepoint; resolve to the violated constraint's name, or null. */
|
|
74
|
-
const violation = async (run: () => Promise<unknown>): Promise<string | null> => {
|
|
75
|
-
await db.query("SAVEPOINT attempt");
|
|
76
|
-
try {
|
|
77
|
-
await run();
|
|
78
|
-
await db.query("RELEASE SAVEPOINT attempt");
|
|
79
|
-
return null;
|
|
80
|
-
} catch (err) {
|
|
81
|
-
await db.query("ROLLBACK TO SAVEPOINT attempt");
|
|
82
|
-
return (err as { constraint?: string }).constraint ?? String(err);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const completions = async () =>
|
|
87
|
-
Object.fromEntries(
|
|
88
|
-
(
|
|
89
|
-
await db.query<{ slug: string; completion: string; checkout_offering: string | null }>(
|
|
90
|
-
`SELECT "slug", "completion", "checkout_offering" FROM "flow_definitions" WHERE "workspace_id" = $1`,
|
|
91
|
-
[WS],
|
|
92
|
-
)
|
|
93
|
-
).rows.map((r) => [r.slug, [r.completion, r.checkout_offering]]),
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
it("the backfill sets the three process magnets to invite and leaves contact and form at lead, idempotently", async () => {
|
|
97
|
-
for (const slug of SLUGS) await insertMagnet(slug);
|
|
98
|
-
const before = await completions();
|
|
99
|
-
for (const slug of SLUGS) expect(before[slug]).toEqual(["lead", null]);
|
|
100
|
-
|
|
101
|
-
await db.query(backfillBlock(MIGRATION_SQL));
|
|
102
|
-
const after = await completions();
|
|
103
|
-
expect(after).toEqual({
|
|
104
|
-
contact: ["lead", null],
|
|
105
|
-
form: ["lead", null],
|
|
106
|
-
onboarding: ["invite", null],
|
|
107
|
-
"request-quote": ["invite", null],
|
|
108
|
-
"job-application": ["invite", null],
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const rerun = await db.query(backfillBlock(MIGRATION_SQL));
|
|
112
|
-
expect(await completions()).toEqual(after);
|
|
113
|
-
// The re-run matches the same rows but stores the same values.
|
|
114
|
-
expect(rerun.rowCount).toBeGreaterThanOrEqual(3);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it.each([
|
|
118
|
-
["checkout with no offering", { completion: "checkout", checkout_offering: null }, "flow_definitions_checkout_offering_pair"],
|
|
119
|
-
["an offering beside lead", { completion: "lead", checkout_offering: "retainer" }, "flow_definitions_checkout_offering_pair"],
|
|
120
|
-
["a capitalised key", { completion: "checkout", checkout_offering: "Bad" }, "flow_definitions_checkout_offering_format"],
|
|
121
|
-
["an underscored key", { completion: "checkout", checkout_offering: "a_b" }, "flow_definitions_checkout_offering_format"],
|
|
122
|
-
["a fourth value", { completion: "other", checkout_offering: null }, "flow_definitions_completion_domain"],
|
|
123
|
-
])("refuses %s", async (_label, cols, constraint) => {
|
|
124
|
-
expect(await violation(() => insertMagnet(`refused-${constraint.length}-${String(cols.completion)}`, cols))).toBe(
|
|
125
|
-
constraint,
|
|
126
|
-
);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("accepts checkout with a listed-shape offering", async () => {
|
|
130
|
-
expect(
|
|
131
|
-
await violation(() =>
|
|
132
|
-
insertMagnet("paid-waitlist", { completion: "checkout", checkout_offering: "retainer" }),
|
|
133
|
-
),
|
|
134
|
-
).toBeNull();
|
|
135
|
-
});
|
|
136
|
-
});
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file flow-workspace-key.db.test.ts
|
|
3
|
-
*
|
|
4
|
-
* LIVE-DATABASE proof of the #6155 backfill (spec § 8.4, AC-1). Requires
|
|
5
|
-
* DATABASE_URL pointing at a Postgres with the migrations applied (`prisma
|
|
6
|
-
* migrate deploy`); the whole suite SKIPS when DATABASE_URL is unset,
|
|
7
|
-
* matching `prefixed-ids.db.test.ts` / `artifact-content-store.db.test.ts`
|
|
8
|
-
* and keeping the workspace Jest run DB-free.
|
|
9
|
-
*
|
|
10
|
-
* Raw SQL through `pg`, for the same reason those two files give: the
|
|
11
|
-
* generated Prisma client is ESM-only and cannot load under this package's
|
|
12
|
-
* CJS ts-jest transform.
|
|
13
|
-
*
|
|
14
|
-
* RECONCILED WITH #6254 (the tenancy-fence keystone, #6373 AC-4). The
|
|
15
|
-
* ORIGINAL "RE-RUN TECHNIQUE" this file used — insert fresh
|
|
16
|
-
* `workspace_id IS NULL` rows, then re-execute this migration's own SQL
|
|
17
|
-
* verbatim to prove its backfill logic — assumed a row COULD land with
|
|
18
|
-
* `workspace_id` unset. That assumption is no longer true on current main:
|
|
19
|
-
* `database/migrations/20260922120000_tenancy_fence` retrofits
|
|
20
|
-
* `flow_definitions.workspace_id` / `flow_sessions.workspace_id` to NOT
|
|
21
|
-
* NULL, plus a `CHECK (workspace_id = site_id)` equality and a
|
|
22
|
-
* `CHECK (workspace_id ~ '^wksp_...')` format constraint on BOTH tables.
|
|
23
|
-
* Together those three constraints mean:
|
|
24
|
-
*
|
|
25
|
-
* - `site_id` must now ALWAYS be a well-formed `wksp_` id equal to
|
|
26
|
-
* `workspace_id` — the legacy alias `"default"` (never `wksp_`-shaped)
|
|
27
|
-
* and an arbitrary unresolvable string can no longer be written at all,
|
|
28
|
-
* with or without `workspace_id` supplied. Attempting either now aborts
|
|
29
|
-
* the write outright (a NOT NULL / CHECK violation), rather than landing
|
|
30
|
-
* a row with `workspace_id` left NULL — this is the fence doing exactly
|
|
31
|
-
* what its own migration text says an unresolvable row must do ("abort
|
|
32
|
-
* the transaction rather than pass through silently").
|
|
33
|
-
* - The `"default"` alias itself is resolved at the APPLICATION layer
|
|
34
|
-
* before a write reaches the database now (the fence migration's own
|
|
35
|
-
* header: "the application-code half of D1a... is fixed in the same
|
|
36
|
-
* PR") — a fresh row's `site_id` is never the literal string `"default"`
|
|
37
|
-
* on live main, only ever the resolved `DEFAULT_WORKSPACE_ID` sentinel.
|
|
38
|
-
*
|
|
39
|
-
* So this suite's three cases are reconciled to what main actually does:
|
|
40
|
-
*
|
|
41
|
-
* 1. A row whose `site_id` names a real workspace carries a matching
|
|
42
|
-
* `workspace_id` from the moment it is written (the shape the OLD
|
|
43
|
-
* backfill used to produce is now enforced at write time), and is
|
|
44
|
-
* readable through the shape `tenantDb(tenant).flows.byId` /
|
|
45
|
-
* `.flowSessions.byId` use (`findFirst({ id, workspaceId })`). The
|
|
46
|
-
* shipped `20260919120000_flow_workspace_key` migration is STILL
|
|
47
|
-
* re-run verbatim afterward — every `workspace_id IS NULL` row it
|
|
48
|
-
* would touch is now unreachable, so this proves the migration stays
|
|
49
|
-
* safely re-runnable (NFR3) rather than corrupting an already-correct
|
|
50
|
-
* row, not that it backfills one.
|
|
51
|
-
* 2. A row whose `site_id` matches no workspace can no longer be created
|
|
52
|
-
* at all — inserting it (with `workspace_id` omitted, exactly the
|
|
53
|
-
* pre-fence shape) is REJECTED by the NOT NULL constraint the fence
|
|
54
|
-
* added, never landed unset.
|
|
55
|
-
* 3. A row whose `site_id` is the resolved sentinel `DEFAULT_WORKSPACE_ID`
|
|
56
|
-
* — what the legacy `"default"` alias now resolves to before any write
|
|
57
|
-
* reaches the database — carries a matching `workspace_id` from the
|
|
58
|
-
* moment it is written, exactly like case 1.
|
|
59
|
-
* 4. The literal sentinel id written into the migration's own SQL text
|
|
60
|
-
* equals `DEFAULT_WORKSPACE_ID` from `../tenancy.js` — read from the
|
|
61
|
-
* `.sql` file and compared, so the two cannot drift apart (§ 5.3
|
|
62
|
-
* step 5). Unaffected by the fence — no DB write involved.
|
|
63
|
-
*/
|
|
64
|
-
import { randomUUID } from "node:crypto";
|
|
65
|
-
import { readFileSync } from "node:fs";
|
|
66
|
-
import { join } from "node:path";
|
|
67
|
-
|
|
68
|
-
import { Client } from "pg";
|
|
69
|
-
|
|
70
|
-
import { DEFAULT_WORKSPACE_ID } from "../tenancy.js";
|
|
71
|
-
|
|
72
|
-
const describeDb = process.env.DATABASE_URL ? describe : describe.skip;
|
|
73
|
-
|
|
74
|
-
/** A run-unique suffix so repeated local runs never collide. */
|
|
75
|
-
const RUN = randomUUID().slice(0, 8);
|
|
76
|
-
|
|
77
|
-
const MIGRATION_SQL_PATH = join(
|
|
78
|
-
__dirname,
|
|
79
|
-
"..",
|
|
80
|
-
"migrations",
|
|
81
|
-
"20260919120000_flow_workspace_key",
|
|
82
|
-
"migration.sql",
|
|
83
|
-
);
|
|
84
|
-
const MIGRATION_SQL = readFileSync(MIGRATION_SQL_PATH, "utf-8");
|
|
85
|
-
|
|
86
|
-
describeDb(
|
|
87
|
-
"flow_definitions / flow_sessions workspace_id backfill (#6155, live DB)",
|
|
88
|
-
() => {
|
|
89
|
-
let db: Client;
|
|
90
|
-
let otherWorkspaceId: string;
|
|
91
|
-
const noSuchSite = `no-such-site-${RUN}`;
|
|
92
|
-
|
|
93
|
-
const definitionIds: string[] = [];
|
|
94
|
-
const sessionIds: string[] = [];
|
|
95
|
-
|
|
96
|
-
beforeAll(async () => {
|
|
97
|
-
db = new Client({ connectionString: process.env.DATABASE_URL });
|
|
98
|
-
await db.connect();
|
|
99
|
-
|
|
100
|
-
// A REAL, non-sentinel workspace — the "pinned, resolvable" case (rule 3:
|
|
101
|
-
// exact `workspaces.id` equality, nothing to do with the sentinel).
|
|
102
|
-
const org = (
|
|
103
|
-
await db.query<{ id: string }>(
|
|
104
|
-
`INSERT INTO "organizations" ("name", "slug", "updated_at")
|
|
105
|
-
VALUES ($1, $2, now()) RETURNING "id"`,
|
|
106
|
-
[`6155 Org ${RUN}`, `6155-org-${RUN}`],
|
|
107
|
-
)
|
|
108
|
-
).rows[0]!;
|
|
109
|
-
otherWorkspaceId = (
|
|
110
|
-
await db.query<{ id: string }>(
|
|
111
|
-
`INSERT INTO "workspaces" ("organization_id", "name", "slug", "updated_at")
|
|
112
|
-
VALUES ($1, $2, $3, now()) RETURNING "id"`,
|
|
113
|
-
[org.id, `6155 Workspace ${RUN}`, `6155-ws-${RUN}`],
|
|
114
|
-
)
|
|
115
|
-
).rows[0]!.id;
|
|
116
|
-
|
|
117
|
-
// One FlowDefinition / FlowSession pair, carrying its correct
|
|
118
|
-
// workspace_id from the moment of insert — the fence's NOT NULL means
|
|
119
|
-
// there is no longer a `workspace_id IS NULL` pre-state to construct
|
|
120
|
-
// for a row that CAN legitimately exist (see the file header). `title`
|
|
121
|
-
// / `status` are also NOT NULL as of the same-day
|
|
122
|
-
// `20260919130000_flow_definition_admin_columns` migration (no column
|
|
123
|
-
// default, by that migration's own design — a nameless/unpublished
|
|
124
|
-
// magnet must never enter the table silently) — both supplied here so
|
|
125
|
-
// the ONLY thing under test is the workspace_id/site_id shape.
|
|
126
|
-
const insertPair = async (siteId: string): Promise<void> => {
|
|
127
|
-
const def = (
|
|
128
|
-
await db.query<{ id: string }>(
|
|
129
|
-
`INSERT INTO "flow_definitions"
|
|
130
|
-
("site_id", "workspace_id", "slug", "config", "title", "status", "updated_at")
|
|
131
|
-
VALUES ($1, $1, $2, '{}'::jsonb, 'Test', 'live', now()) RETURNING "id"`,
|
|
132
|
-
[siteId, `6155-${RUN}-${siteId}`],
|
|
133
|
-
)
|
|
134
|
-
).rows[0]!;
|
|
135
|
-
definitionIds.push(def.id);
|
|
136
|
-
|
|
137
|
-
const sess = (
|
|
138
|
-
await db.query<{ id: string }>(
|
|
139
|
-
`INSERT INTO "flow_sessions"
|
|
140
|
-
("site_id", "workspace_id", "flow_id", "flow_slug", "config_snapshot", "updated_at")
|
|
141
|
-
VALUES ($1, $1, $2, $3, '{}'::jsonb, now()) RETURNING "id"`,
|
|
142
|
-
[siteId, def.id, `6155-${RUN}-${siteId}`],
|
|
143
|
-
)
|
|
144
|
-
).rows[0]!;
|
|
145
|
-
sessionIds.push(sess.id);
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Case 1's row FIRST, then the migration re-run, THEN case 3's row —
|
|
149
|
-
// deliberately sequenced, not merged into one loop. The migration's
|
|
150
|
-
// OWN post-assertions (re-read verbatim below) reject ANY row whose
|
|
151
|
-
// workspace_id names a workspace absent from "workspaces" ("nothing
|
|
152
|
-
// guessed"); `DEFAULT_WORKSPACE_ID` has no corresponding "workspaces"
|
|
153
|
-
// row on a freshly-migrated-but-unseeded database (that row is
|
|
154
|
-
// `database/seed.ts`'s job, not a migration's), so the re-run must
|
|
155
|
-
// happen BEFORE case 3's row exists, or it would trip that guard for
|
|
156
|
-
// a row it never touched.
|
|
157
|
-
await insertPair(otherWorkspaceId);
|
|
158
|
-
|
|
159
|
-
// Re-run the shipped migration file verbatim (NFR3 — still
|
|
160
|
-
// re-runnable). Every `workspace_id IS NULL` row it would touch is now
|
|
161
|
-
// unreachable (the fence's NOT NULL forbids one from ever existing),
|
|
162
|
-
// so this is a no-op on the row just inserted — proven by the
|
|
163
|
-
// "unchanged after re-run" assertion in the first `it()` block below,
|
|
164
|
-
// not a backfill.
|
|
165
|
-
await db.query(MIGRATION_SQL);
|
|
166
|
-
|
|
167
|
-
await insertPair(DEFAULT_WORKSPACE_ID);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
afterAll(async () => {
|
|
171
|
-
await db.query(`DELETE FROM "flow_sessions" WHERE "flow_slug" LIKE $1`, [
|
|
172
|
-
`6155-${RUN}-%`,
|
|
173
|
-
]);
|
|
174
|
-
await db.query(`DELETE FROM "flow_definitions" WHERE "slug" LIKE $1`, [
|
|
175
|
-
`6155-${RUN}-%`,
|
|
176
|
-
]);
|
|
177
|
-
await db.query(`DELETE FROM "workspaces" WHERE "slug" = $1`, [
|
|
178
|
-
`6155-ws-${RUN}`,
|
|
179
|
-
]);
|
|
180
|
-
await db.query(`DELETE FROM "organizations" WHERE "slug" = $1`, [
|
|
181
|
-
`6155-org-${RUN}`,
|
|
182
|
-
]);
|
|
183
|
-
await db.end();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("a row whose site_id names a real workspace carries a matching workspace_id, unchanged by a migration re-run, and is readable through the accessor's own query shape", async () => {
|
|
187
|
-
const [defId, sessId] = [definitionIds[0]!, sessionIds[0]!];
|
|
188
|
-
|
|
189
|
-
const def = await db.query<{ workspace_id: string | null }>(
|
|
190
|
-
`SELECT "workspace_id" FROM "flow_definitions" WHERE "id" = $1`,
|
|
191
|
-
[defId],
|
|
192
|
-
);
|
|
193
|
-
expect(def.rows[0]!.workspace_id).toBe(otherWorkspaceId);
|
|
194
|
-
|
|
195
|
-
// The shape tenantDb(tenant).flows.byId uses: findFirst({ id, workspaceId }).
|
|
196
|
-
const readable = await db.query(
|
|
197
|
-
`SELECT "id" FROM "flow_definitions" WHERE "id" = $1 AND "workspace_id" = $2`,
|
|
198
|
-
[defId, otherWorkspaceId],
|
|
199
|
-
);
|
|
200
|
-
expect(readable.rows).toHaveLength(1);
|
|
201
|
-
|
|
202
|
-
const sess = await db.query<{ workspace_id: string | null }>(
|
|
203
|
-
`SELECT "workspace_id" FROM "flow_sessions" WHERE "id" = $1`,
|
|
204
|
-
[sessId],
|
|
205
|
-
);
|
|
206
|
-
expect(sess.rows[0]!.workspace_id).toBe(otherWorkspaceId);
|
|
207
|
-
|
|
208
|
-
const sessReadable = await db.query(
|
|
209
|
-
`SELECT "id" FROM "flow_sessions" WHERE "id" = $1 AND "workspace_id" = $2`,
|
|
210
|
-
[sessId, otherWorkspaceId],
|
|
211
|
-
);
|
|
212
|
-
expect(sessReadable.rows).toHaveLength(1);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("a row whose site_id matches no workspace can no longer be created — the fence retrofit rejects it outright, never leaving it unset", async () => {
|
|
216
|
-
// The EXACT pre-fence shape (no workspace_id supplied) — proving that
|
|
217
|
-
// what used to land a `workspace_id IS NULL` row is now rejected
|
|
218
|
-
// before it can, on both tables the #6155 backfill covered.
|
|
219
|
-
await expect(
|
|
220
|
-
db.query(
|
|
221
|
-
`INSERT INTO "flow_definitions" ("site_id", "slug", "config", "title", "status", "updated_at")
|
|
222
|
-
VALUES ($1, $2, '{}'::jsonb, 'Test', 'live', now())`,
|
|
223
|
-
[noSuchSite, `6155-${RUN}-abort-def`],
|
|
224
|
-
),
|
|
225
|
-
).rejects.toThrow(/workspace_id/);
|
|
226
|
-
|
|
227
|
-
await expect(
|
|
228
|
-
db.query(
|
|
229
|
-
`INSERT INTO "flow_sessions" ("site_id", "flow_id", "flow_slug", "config_snapshot", "updated_at")
|
|
230
|
-
VALUES ($1, $2, $3, '{}'::jsonb, now())`,
|
|
231
|
-
[noSuchSite, definitionIds[0]!, `6155-${RUN}-abort-sess`],
|
|
232
|
-
),
|
|
233
|
-
).rejects.toThrow(/workspace_id/);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("a row whose site_id is the resolved sentinel DEFAULT_WORKSPACE_ID carries a matching workspace_id (D2 rule 2's write-time destination)", async () => {
|
|
237
|
-
const [defId, sessId] = [definitionIds[1]!, sessionIds[1]!];
|
|
238
|
-
|
|
239
|
-
const def = await db.query<{ workspace_id: string | null }>(
|
|
240
|
-
`SELECT "workspace_id" FROM "flow_definitions" WHERE "id" = $1`,
|
|
241
|
-
[defId],
|
|
242
|
-
);
|
|
243
|
-
expect(def.rows[0]!.workspace_id).toBe(DEFAULT_WORKSPACE_ID);
|
|
244
|
-
|
|
245
|
-
const sess = await db.query<{ workspace_id: string | null }>(
|
|
246
|
-
`SELECT "workspace_id" FROM "flow_sessions" WHERE "id" = $1`,
|
|
247
|
-
[sessId],
|
|
248
|
-
);
|
|
249
|
-
expect(sess.rows[0]!.workspace_id).toBe(DEFAULT_WORKSPACE_ID);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("the migration's own literal sentinel id equals DEFAULT_WORKSPACE_ID — the two cannot drift apart", () => {
|
|
253
|
-
// SQL cannot import database/tenancy.ts (§ 5.3 step 5), so the migration
|
|
254
|
-
// carries the sentinel as a literal. Pin it against the exported
|
|
255
|
-
// constant by reading the shipped file, not a re-typed copy of it.
|
|
256
|
-
const occurrences = MIGRATION_SQL.match(/'wksp_[0-9a-f-]+'/g) ?? [];
|
|
257
|
-
expect(occurrences.length).toBeGreaterThan(0);
|
|
258
|
-
for (const literal of occurrences) {
|
|
259
|
-
expect(literal).toBe(`'${DEFAULT_WORKSPACE_ID}'`);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
},
|
|
263
|
-
);
|