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,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file site.ts
|
|
3
|
-
* @description The current-site resolution helper (#3958 §3.2), extended by
|
|
4
|
-
* #6155 (spec D2/D5/D6, ownership brief
|
|
5
|
-
* `docs/internal/decisions/workspace-owns-data-surfaces-render.md`) with the
|
|
6
|
-
* ALIAS -> WORKSPACE resolution the brief requires: `currentSiteId()`'s
|
|
7
|
-
* env-var alias is resolved to a real `workspaces.id` ONCE at startup,
|
|
8
|
-
* memoized, and ASSERTED — so a misconfigured deployment refuses to boot
|
|
9
|
-
* instead of serving empty lists (AC-5).
|
|
10
|
-
*
|
|
11
|
-
* `currentSiteId()` is extracted verbatim from `routes/flows/index.ts`'s
|
|
12
|
-
* private `currentSiteId()` so the flow-preset seed
|
|
13
|
-
* (`services/flow-engine.ts` -> `bin/seed-presets.ts`) resolves the SAME
|
|
14
|
-
* site id the `/v1/flows/*` routes resolve — a seed hard-coding `"default"`
|
|
15
|
-
* would write presets the API cannot find whenever `AILK_SITE_ID` is set (a
|
|
16
|
-
* silent, environment-dependent 404). Behavior is byte-identical to the
|
|
17
|
-
* pre-extraction helper: same env var, same fallback, same call sites.
|
|
18
|
-
* `currentSiteId()` stays SYNCHRONOUS and byte-identical (#6155 D5) — the
|
|
19
|
-
* new resolution is async (it reads the database), and making
|
|
20
|
-
* `currentSiteId()` async would edit every one of its dozen non-test call
|
|
21
|
-
* sites across routes/services/bins, including the two route files NFR1
|
|
22
|
-
* requires to stay byte-unchanged.
|
|
23
|
-
*
|
|
24
|
-
* The Checkout site stamp (#7868, spec D1/D2): `SITE_STAMP_KEY`,
|
|
25
|
-
* `siteStamp()` and `isForeignSiteStamp()` are the one definition of the
|
|
26
|
-
* `metadata.site` key every hub-created Checkout session carries and the
|
|
27
|
-
* Stripe webhook reads. The stamp is `currentSiteId()`, so writer and reader
|
|
28
|
-
* cannot disagree inside one deployment. A session is foreign only when its
|
|
29
|
-
* stamp is a non-empty string other than `currentSiteId()`; an unstamped
|
|
30
|
-
* session is the hub's own (roadmap D4).
|
|
31
|
-
*/
|
|
32
|
-
import { resolvedAdapterKind } from "@working-theory/content-adapters";
|
|
33
|
-
import {
|
|
34
|
-
DEFAULT_WORKSPACE_ID,
|
|
35
|
-
DEFAULT_WORKSPACE_SLUG,
|
|
36
|
-
resolveSentinelWorkspaceRow,
|
|
37
|
-
} from "@working-theory/database/tenancy";
|
|
38
|
-
import { logger } from "@working-theory/observability";
|
|
39
|
-
|
|
40
|
-
import { prisma } from "./prisma.js";
|
|
41
|
-
|
|
42
|
-
export function currentSiteId(): string {
|
|
43
|
-
return process.env.AILK_SITE_ID ?? "default";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** The Checkout session metadata key naming the site a session belongs to (#7868 D1). */
|
|
47
|
-
export const SITE_STAMP_KEY = "site";
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The metadata a hub-created Checkout session carries (#7868 D1/D2):
|
|
51
|
-
* `{ [SITE_STAMP_KEY]: currentSiteId() }`, read per call. Empty when the alias
|
|
52
|
-
* is `""` (an `AILK_SITE_ID` set to the empty string), so Stripe is never sent
|
|
53
|
-
* an empty value (SEC-7868-5) — and the webhook treats that deployment's
|
|
54
|
-
* sessions as unstamped, exactly as today.
|
|
55
|
-
*/
|
|
56
|
-
export function siteStamp(): Record<string, string> {
|
|
57
|
-
const site = currentSiteId();
|
|
58
|
-
return site === "" ? {} : { [SITE_STAMP_KEY]: site };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* True only when `metadata[SITE_STAMP_KEY]` is a non-empty string that is not
|
|
63
|
-
* `currentSiteId()` — a session another site on the shared Stripe account
|
|
64
|
-
* opened (#7868 D2). An absent, empty or non-string stamp is the hub's own
|
|
65
|
-
* (roadmap D4: Payment Links, invoices, the portal and pre-stamp sessions
|
|
66
|
-
* carry none). Exact, case-sensitive comparison, as the spokes' own check.
|
|
67
|
-
*/
|
|
68
|
-
export function isForeignSiteStamp(
|
|
69
|
-
metadata: Record<string, string> | null | undefined,
|
|
70
|
-
): boolean {
|
|
71
|
-
const stamp: unknown = metadata?.[SITE_STAMP_KEY];
|
|
72
|
-
return typeof stamp === "string" && stamp !== "" && stamp !== currentSiteId();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* The resolved workspace id, memoized after the first SUCCESSFUL resolution
|
|
77
|
-
* ("resolves the alias once at startup", ownership brief § Layer 2 / #6155
|
|
78
|
-
* D5). `null` is never cached — a failed resolution has already thrown from
|
|
79
|
-
* `build()` (D6), so there is no negative-cache case to hold, and a later
|
|
80
|
-
* retry (a test, a re-seeded database) is free to resolve on its next call.
|
|
81
|
-
*/
|
|
82
|
-
let cachedWorkspaceId: string | null = null;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Resolves `currentSiteId()`'s alias to a real `workspaces.id`, per the
|
|
86
|
-
* two-and-only-two rule (#6155 spec D2):
|
|
87
|
-
*
|
|
88
|
-
* 1. The literal alias `"default"` resolves to the seeded sentinel
|
|
89
|
-
* workspace — `DEFAULT_WORKSPACE_ID` — IFF that row exists and hangs
|
|
90
|
-
* off the sentinel Organization
|
|
91
|
-
* (`@working-theory/database/tenancy`'s `resolveSentinelWorkspaceRow`,
|
|
92
|
-
* which carries NO `workspace.count()` gate — unlike
|
|
93
|
-
* `resolveDefaultWorkspaceRow`, whose single-tenancy meaning this unit
|
|
94
|
-
* must NOT widen; see D2's "why a NEW resolver"). This is what lets a
|
|
95
|
-
* correctly-configured MULTI-workspace deployment that has not pinned
|
|
96
|
-
* `AILK_SITE_ID` still resolve — `resolveDefaultWorkspaceRow`'s count
|
|
97
|
-
* gate would refuse it.
|
|
98
|
-
* 2. Any OTHER alias resolves ONLY by exact `workspaces.id` equality.
|
|
99
|
-
* 3. Anything that resolves neither way is `null` — never a fallback,
|
|
100
|
-
* never a guess (`assertSiteAliasResolves` below is what turns a `null`
|
|
101
|
-
* into a startup refusal).
|
|
102
|
-
*
|
|
103
|
-
* Memoized (D5): the SECOND call issues zero Prisma queries once the first
|
|
104
|
-
* has resolved successfully. `__resetSiteWorkspaceCacheForTests()` clears
|
|
105
|
-
* the memo for test isolation.
|
|
106
|
-
*/
|
|
107
|
-
export async function resolveSiteWorkspaceId(): Promise<string | null> {
|
|
108
|
-
if (cachedWorkspaceId !== null) {
|
|
109
|
-
return cachedWorkspaceId;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const resolved = await resolveWorkspaceIdForAlias(currentSiteId());
|
|
113
|
-
if (resolved !== null) {
|
|
114
|
-
cachedWorkspaceId = resolved;
|
|
115
|
-
}
|
|
116
|
-
return resolved;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* The two-and-only-two resolution rule (#6155 spec D2), extracted so a
|
|
121
|
-
* write site holding an EXPLICIT `siteId` parameter (not necessarily
|
|
122
|
-
* `currentSiteId()` — e.g. a `ProjectListing` resolved through its own
|
|
123
|
-
* `SiteListingConfig.siteId`) can resolve it the same way
|
|
124
|
-
* `resolveSiteWorkspaceId()` resolves the env alias, without going through
|
|
125
|
-
* that function's `currentSiteId()`-only memo (#6254 spec D1a): the write
|
|
126
|
-
* paths that still stamp a raw, unresolved alias resolve it first, through
|
|
127
|
-
* this resolver, before it ever reaches a `workspace_id NOT NULL` /
|
|
128
|
-
* `CHECK (workspace_id = site_id)` column.
|
|
129
|
-
*
|
|
130
|
-
* 1. The literal alias `"default"` resolves to the seeded sentinel
|
|
131
|
-
* workspace IFF that row exists (`resolveSentinelWorkspaceRow`).
|
|
132
|
-
* 2. Any OTHER alias resolves ONLY by exact `workspaces.id` equality.
|
|
133
|
-
* 3. Anything that resolves neither way is `null` — never a fallback,
|
|
134
|
-
* never a guess.
|
|
135
|
-
*
|
|
136
|
-
* Not memoized — callers passing a per-request `siteId` would otherwise
|
|
137
|
-
* share a cache keyed on nothing. `resolveSiteWorkspaceId()` above keeps
|
|
138
|
-
* its own single-value memo for the hot, env-alias-only path.
|
|
139
|
-
*/
|
|
140
|
-
export async function resolveWorkspaceIdForAlias(
|
|
141
|
-
alias: string,
|
|
142
|
-
): Promise<string | null> {
|
|
143
|
-
if (alias === DEFAULT_WORKSPACE_SLUG) {
|
|
144
|
-
const row = await resolveSentinelWorkspaceRow(prisma);
|
|
145
|
-
return row?.id ?? null;
|
|
146
|
-
}
|
|
147
|
-
const row = await prisma.workspace.findUnique({
|
|
148
|
-
where: { id: alias },
|
|
149
|
-
select: { id: true },
|
|
150
|
-
});
|
|
151
|
-
return row ? alias : null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Asserts `currentSiteId()`'s alias resolves to a real workspace — the
|
|
156
|
-
* misconfigured-deployment refusal AC-5 requires (D6). Awaited from
|
|
157
|
-
* `build()` (`apps/api/src/server.ts`), beside the two startup assertions
|
|
158
|
-
* already there; `apps/api/src/index.ts` already catches, logs and
|
|
159
|
-
* `process.exit(1)`s on a `build()` throw, so this is a loud refusal to
|
|
160
|
-
* start with no new exit-path code.
|
|
161
|
-
*
|
|
162
|
-
* THE CARVE-OUT (D6): when the alias genuinely CANNOT be checked — or NEED
|
|
163
|
-
* not be — this logs a warning and returns rather than throwing: a
|
|
164
|
-
* "database-less dev boot," never a misconfiguration. Three arms feed it, and
|
|
165
|
-
* ALL THREE are presence checks made BEFORE any query is attempted — never a
|
|
166
|
-
* catch around a query that already ran, which would just as readily swallow a
|
|
167
|
-
* genuine connection failure as it would an absent delegate (code-review
|
|
168
|
-
* finding, #6155):
|
|
169
|
-
*
|
|
170
|
-
* 0. **The content adapter is not `database`** (#6228) — the one arm that is
|
|
171
|
-
* "need not," not "cannot." The alias→workspace resolution exists so a
|
|
172
|
-
* database-backed content read has a workspace to read within; under
|
|
173
|
-
* `mdx` or `projection` NO read takes a `workspaceId` at all, so the
|
|
174
|
-
* assertion guards nothing and its only effect is to require a REACHABLE
|
|
175
|
-
* database of a deployment that never queries one. That is the #6228
|
|
176
|
-
* defect: `create-ailk`'s scaffold check sets a placeholder
|
|
177
|
-
* `DATABASE_URL` (presence is all `validateConfig()` wants, and Prisma
|
|
178
|
-
* connects lazily), so arm 1 does not fire, and the generated MDX site
|
|
179
|
-
* crashed at boot on `Can't reach database server`. Read through
|
|
180
|
-
* `resolvedAdapterKind()` — the ONE switch over `CONTENT_ADAPTER`
|
|
181
|
-
* (`packages/content-adapters/src/resolve.ts`) — rather than a second,
|
|
182
|
-
* drifting read of the env var here.
|
|
183
|
-
*
|
|
184
|
-
* 1. **No `DATABASE_URL` configured** — the literal `requireDb()` guard
|
|
185
|
-
* `apps/api/src/services/flow-engine.ts` uses for the flow services:
|
|
186
|
-
* presence, not reachability. (`apps/api/src/config/index.ts`'s own
|
|
187
|
-
* eager `validateConfig()` refuses a **production** `apps/api` boot with
|
|
188
|
-
* `DATABASE_URL` unset before `build()` is ever reached; outside
|
|
189
|
-
* production a real boot starts database-less (#6482), so this arm is
|
|
190
|
-
* reachable from a real dev boot as well as from any other caller of
|
|
191
|
-
* `build()`.)
|
|
192
|
-
* 2. **`prisma.workspace.findUnique` is not even a function** — the case
|
|
193
|
-
* that surfaced this arm: `apps/api`'s shared/global prisma mocks
|
|
194
|
-
* (`lib/__mocks__/prisma-client.js`, the module-map-level double every
|
|
195
|
-
* test gets; the many hand-rolled hoisted factories in route/service
|
|
196
|
-
* suites that never touch tenancy) predate this unit and declare no
|
|
197
|
-
* `workspace` delegate at all — the FIRST real Prisma read `build()`
|
|
198
|
-
* itself has ever issued. Widening dozens of unrelated test files'
|
|
199
|
-
* prisma doubles to add a resolving `workspace.findUnique` has a far
|
|
200
|
-
* larger and harder-to-audit blast radius than making THIS one
|
|
201
|
-
* assertion resilient to "the check itself is unavailable." A resolution
|
|
202
|
-
* that could not even be ATTEMPTED is not a misconfiguration to fail
|
|
203
|
-
* loudly about — but a resolution that WAS attempted and threw (a real
|
|
204
|
-
* connection error, a permissions error, a bug in the resolver) is
|
|
205
|
-
* exactly the class D6/AC-5 exist to surface, so it is deliberately NOT
|
|
206
|
-
* caught here: it propagates out of `resolveSiteWorkspaceId()` and
|
|
207
|
-
* crashes the boot, the same as any other unhandled `build()` throw.
|
|
208
|
-
*
|
|
209
|
-
* A reachable database with no matching workspace is the failure case, and
|
|
210
|
-
* it throws. A reachable database that ERRORS mid-query throws too — that is
|
|
211
|
-
* NOT the carve-out, it is the misconfiguration AC-5 asks to fail loudly
|
|
212
|
-
* about.
|
|
213
|
-
*/
|
|
214
|
-
export async function assertSiteAliasResolves(): Promise<void> {
|
|
215
|
-
const adapterKind = resolvedAdapterKind();
|
|
216
|
-
if (adapterKind !== "database") {
|
|
217
|
-
logger.warn(
|
|
218
|
-
{ alias: currentSiteId(), contentAdapter: adapterKind },
|
|
219
|
-
"[site] content adapter is not `database` — skipping the site-alias workspace assertion (no read takes a workspaceId on this boot)",
|
|
220
|
-
);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (!process.env.DATABASE_URL) {
|
|
225
|
-
logger.warn(
|
|
226
|
-
{ alias: currentSiteId() },
|
|
227
|
-
"[site] DATABASE_URL not set — skipping the site-alias workspace assertion (database-less boot)",
|
|
228
|
-
);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (typeof prisma.workspace?.findUnique !== "function") {
|
|
233
|
-
logger.warn(
|
|
234
|
-
{ alias: currentSiteId() },
|
|
235
|
-
"[site] prisma.workspace is unavailable — skipping the site-alias workspace assertion (the resolution could not be attempted)",
|
|
236
|
-
);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const resolved = await resolveSiteWorkspaceId();
|
|
241
|
-
if (resolved !== null) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const alias = currentSiteId();
|
|
246
|
-
throw new Error(
|
|
247
|
-
`AILK_SITE_ID="${alias}" names no workspace. The site alias must be an ` +
|
|
248
|
-
'existing workspaces.id, or the literal "default" (the seeded ' +
|
|
249
|
-
`sentinel workspace ${DEFAULT_WORKSPACE_ID}). Run \`pnpm db:seed\`, or ` +
|
|
250
|
-
"set AILK_SITE_ID to a workspace this database holds.",
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** Test-only: clears the memoized resolution (#6155 D5). */
|
|
255
|
-
export function __resetSiteWorkspaceCacheForTests(): void {
|
|
256
|
-
cachedWorkspaceId = null;
|
|
257
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Slice-module load boundary (#5334 D4).
|
|
3
|
-
*
|
|
4
|
-
* Every paid-module SLICE in `apps/api` is reached through an indirect dynamic
|
|
5
|
-
* `import()` — a specifier the call site never writes as a literal, because
|
|
6
|
-
* `tsc` would then resolve it and the OSS strip would break (#5182 D1). #5304
|
|
7
|
-
* moved those specifiers onto module-level constants, the one shape that is
|
|
8
|
-
* simultaneously unresolvable to `tsc` and traceable to `@vercel/nft`, so the
|
|
9
|
-
* files reach a Vercel deployment.
|
|
10
|
-
*
|
|
11
|
-
* WHAT THIS FILE IS FOR. That shape is correct for the deploy path the scaffold
|
|
12
|
-
* ships, and it cannot be made to work under a BUNDLER. esbuild bundles a
|
|
13
|
-
* dynamic `import()` iff its argument is a string literal at the call site —
|
|
14
|
-
* exactly the predicate `tsc` resolves on, and exactly the one #5182 D1
|
|
15
|
-
* requires be false. So in a bundled build the specifier survives verbatim and
|
|
16
|
-
* Node resolves it against the BUNDLE OUTPUT directory rather than the source
|
|
17
|
-
* module's: `"./list.js"`, written in `routes/project-listings/index.ts`,
|
|
18
|
-
* becomes `dist/list.js` and does not exist. `build()` throws during
|
|
19
|
-
* CONSTRUCTION, so every route 500s rather than one.
|
|
20
|
-
*
|
|
21
|
-
* The failure is otherwise silent on every channel: esbuild emits no warning
|
|
22
|
-
* for the imports it declines to bundle, and a consuming frontend degrades to
|
|
23
|
-
* an empty list and still answers 200. `salessmyth/samhenry` spent nine hours
|
|
24
|
-
* on it. This boundary is what turns that into a one-line diagnosis — it does
|
|
25
|
-
* not make bundling work, because nothing in this file could.
|
|
26
|
-
*
|
|
27
|
-
* WHY IT TAKES A THUNK AND NOT A SPECIFIER. `loadSlice("./list.js")` doing the
|
|
28
|
-
* `import()` in here would be the obvious signature and would silently undo
|
|
29
|
-
* #5304. `enumerateSliceSpecifiers()` treats a file as an "indirect file" only
|
|
30
|
-
* if it carries a dynamic `import()` with a non-literal argument; move the call
|
|
31
|
-
* out of the callers and they stop qualifying, their specifier constants stop
|
|
32
|
-
* being candidates, and `check-slice-reachability.ts` passes vacuously over an
|
|
33
|
-
* empty set. Worse, `@vercel/nft` constant-propagates `WORKSPACES_INDEX` into
|
|
34
|
-
* `import(WORKSPACES_INDEX)` only because the constant and the call sit in one
|
|
35
|
-
* module — hoist the call and there is nothing to propagate, so every slice
|
|
36
|
-
* file goes untraced and #5304's outage returns with its own gate green.
|
|
37
|
-
*
|
|
38
|
-
* So the `import()` stays at the call site, in the caller's module, and this
|
|
39
|
-
* function only awaits the thunk and interprets the failure.
|
|
40
|
-
*
|
|
41
|
-
* Gated by `scripts/check-bundled-build-diagnoses.ts`, which bundles `apps/api`
|
|
42
|
-
* and asserts that the boot fails WITH this diagnostic rather than without one.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Node reports an unresolvable ESM specifier as `ERR_MODULE_NOT_FOUND` and an
|
|
47
|
-
* unresolvable CJS one as `MODULE_NOT_FOUND`. A bundle can produce either,
|
|
48
|
-
* since a bundled graph mixes both.
|
|
49
|
-
*/
|
|
50
|
-
const MODULE_NOT_FOUND_CODES = new Set(["ERR_MODULE_NOT_FOUND", "MODULE_NOT_FOUND"]);
|
|
51
|
-
|
|
52
|
-
function isModuleNotFound(error: unknown): boolean {
|
|
53
|
-
const code = (error as { code?: unknown } | null | undefined)?.code;
|
|
54
|
-
return typeof code === "string" && MODULE_NOT_FOUND_CODES.has(code);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Await a slice module's loader thunk, turning an unresolvable module into a
|
|
59
|
-
* diagnosis instead of a bare `ERR_MODULE_NOT_FOUND` on a path that never
|
|
60
|
-
* existed.
|
|
61
|
-
*
|
|
62
|
-
* Anything that is not a module-resolution failure is rethrown UNTOUCHED — the
|
|
63
|
-
* same object, not a wrapper. A slice that throws while evaluating is a real
|
|
64
|
-
* bug in the slice and must keep its own stack and type.
|
|
65
|
-
*
|
|
66
|
-
* @param specifier the module specifier the thunk imports, for the message.
|
|
67
|
-
* Every call site already has it in scope, so nothing is enumerated here.
|
|
68
|
-
* @param load the caller's own `() => import(CONST)` thunk. Must close over a
|
|
69
|
-
* constant in the CALLER's module — see the file header.
|
|
70
|
-
*/
|
|
71
|
-
export async function loadSlice(
|
|
72
|
-
specifier: string,
|
|
73
|
-
load: () => Promise<unknown>,
|
|
74
|
-
): Promise<unknown> {
|
|
75
|
-
try {
|
|
76
|
-
return await load();
|
|
77
|
-
} catch (error) {
|
|
78
|
-
if (!isModuleNotFound(error)) throw error;
|
|
79
|
-
throw new Error(
|
|
80
|
-
`AILK slice module '${specifier}' could not be resolved at runtime. ` +
|
|
81
|
-
`This throws during server construction, so every route fails, not one. Two causes produce it:\n` +
|
|
82
|
-
` 1. The module was compiled but not SHIPPED — the deploy-time file tracer did not emit it (#5304). ` +
|
|
83
|
-
`Check with \`pnpm slice-reachability:check\`.\n` +
|
|
84
|
-
` 2. This build was BUNDLED. apps/api does not support bundling (#5334): a bundler resolves this ` +
|
|
85
|
-
`specifier against the bundle's own output directory rather than its source module's, and esbuild ` +
|
|
86
|
-
`leaves it unbundled without a warning. See apps/api/CLAUDE.md § Deploy path.`,
|
|
87
|
-
{ cause: error },
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The hub's spoke registry — its ONE data-access boundary (#7532, spec
|
|
3
|
-
* docs/specs/7532-a-spoke-s-public-reads-spec.md D1, D2, D7).
|
|
4
|
-
*
|
|
5
|
-
* A spoke is a separately deployed site that calls this API server-to-server,
|
|
6
|
-
* bound to exactly one workspace. It authenticates with a hub-minted key sent
|
|
7
|
-
* in `x-ailk-spoke-key`. This file is the only one that names `prisma.spoke`.
|
|
8
|
-
*
|
|
9
|
-
* THE KEY IS NEVER STORED. The row keeps the lowercase hex SHA-256 of the
|
|
10
|
-
* trimmed key (`credentialHash`) and its first 17 characters
|
|
11
|
-
* (`credentialPrefix`: `ailk_spk_` plus 8 random ones) so an operator can
|
|
12
|
-
* recognise a key in a list. A fast digest is enough: the key carries 256
|
|
13
|
-
* bits of entropy, so a lookup by its unique digest leaks nothing usable.
|
|
14
|
-
*
|
|
15
|
-
* WHAT A KEY PROVES. It identifies a deployed SITE, not a person: every
|
|
16
|
-
* anonymous visitor of that site, and every agent using its `apps/mcp`, acts
|
|
17
|
-
* through it. It grants what the site's anonymous visitors can already do on
|
|
18
|
-
* the hub — read that workspace's enabled meeting types and public content,
|
|
19
|
-
* and write leads and bookings into it. One opt-in addition (#7876 D3): while
|
|
20
|
-
* the hub's operator sets `AILK_SPOKE_FLOW_SEED`, a key may also write its
|
|
21
|
-
* own workspace's flow definitions (`PUT /v1/flows/:slug/definition`),
|
|
22
|
-
* limited to the blocks the site's own flow pages render — never emails,
|
|
23
|
-
* deliverables, pricing, registers or listings.
|
|
24
|
-
*
|
|
25
|
-
* Three kinds of caller, kept apart:
|
|
26
|
-
* - the public resolver (`services/public-site-resolver.ts`) calls
|
|
27
|
-
* `hashSpokeKey`, `findSpokeByKeyHash` and `hasRegisteredSpoke` only;
|
|
28
|
-
* - the operator CLI (`bin/spokes.ts`) calls `registerSpoke`, `revokeSpoke`
|
|
29
|
-
* and `listSpokes` (and, through `lib/hub-sign-in-store.ts`'s
|
|
30
|
-
* `registerSignInClient`, `findSpokeById`). No route imports them;
|
|
31
|
-
* - the operator flow-copy service (`services/flow-copy.ts`, run only by
|
|
32
|
-
* `bin/flows-copy.ts`) calls `hasLiveSpokeForWorkspace` only (#7844 D7);
|
|
33
|
-
* - the hub sign-in service (`services/hub-sign-in.ts`) and the auth
|
|
34
|
-
* preHandler's hub-credential branch call `hashSpokeKey`,
|
|
35
|
-
* `findSpokeByKeyHash` and `findSpokeById` (#7894 D5, D6).
|
|
36
|
-
*
|
|
37
|
-
* Nothing here caches a row: a revocation takes effect on the next request
|
|
38
|
-
* (NFR4). The resolver holds `hasRegisteredSpoke`'s answer itself (#7633 D3).
|
|
39
|
-
*/
|
|
40
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
41
|
-
|
|
42
|
-
import { prisma } from "./prisma.js";
|
|
43
|
-
|
|
44
|
-
/** Every spoke key starts with this, so a leaked one is recognisable. */
|
|
45
|
-
export const SPOKE_KEY_PREFIX = "ailk_spk_";
|
|
46
|
-
|
|
47
|
-
/** How much of the key the row keeps for recognition: the prefix + 8 characters. */
|
|
48
|
-
const CREDENTIAL_PREFIX_LENGTH = SPOKE_KEY_PREFIX.length + 8;
|
|
49
|
-
|
|
50
|
-
/** A new key: `ailk_spk_` + 32 random bytes in base64url (52 characters). */
|
|
51
|
-
export function mintSpokeKey(): string {
|
|
52
|
-
return `${SPOKE_KEY_PREFIX}${randomBytes(32).toString("base64url")}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** The stored digest of a key: lowercase hex SHA-256 of the TRIMMED value. */
|
|
56
|
-
export function hashSpokeKey(key: string): string {
|
|
57
|
-
return createHash("sha256").update(key.trim()).digest("hex");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface SpokeLookupRow {
|
|
61
|
-
id: string;
|
|
62
|
-
workspaceId: string;
|
|
63
|
-
revokedAt: Date | null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** The one read the resolver makes: a `findUnique` by digest. */
|
|
67
|
-
export async function findSpokeByKeyHash(
|
|
68
|
-
credentialHash: string,
|
|
69
|
-
): Promise<SpokeLookupRow | null> {
|
|
70
|
-
return prisma.spoke.findUnique({
|
|
71
|
-
where: { credentialHash },
|
|
72
|
-
select: { id: true, workspaceId: true, revokedAt: true },
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The sign-in client's read of its own spoke (#7894 D3, D5): a spoke is
|
|
78
|
-
* `Spoke.id`-bound to its `OAuthClient`, and the codes route — which has a
|
|
79
|
-
* cookie session and no spoke key — checks the spoke is still unrevoked by id.
|
|
80
|
-
*/
|
|
81
|
-
export async function findSpokeById(
|
|
82
|
-
id: string,
|
|
83
|
-
): Promise<(SpokeLookupRow & { name: string }) | null> {
|
|
84
|
-
return prisma.spoke.findUnique({
|
|
85
|
-
where: { id },
|
|
86
|
-
select: { id: true, workspaceId: true, revokedAt: true, name: true },
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Whether this deployment is a hub: its registry holds at least one spoke,
|
|
92
|
-
* revoked or not (#7633 D1). Reads every time; the resolver holds the
|
|
93
|
-
* answer (D3).
|
|
94
|
-
*/
|
|
95
|
-
export async function hasRegisteredSpoke(): Promise<boolean> {
|
|
96
|
-
const row = await prisma.spoke.findFirst({ select: { id: true } });
|
|
97
|
-
return Boolean(row);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Whether at least one unrevoked spoke is bound to this workspace (#7844 D7):
|
|
102
|
-
* the flow-copy tool's target check. Selects `id` only.
|
|
103
|
-
*/
|
|
104
|
-
export async function hasLiveSpokeForWorkspace(
|
|
105
|
-
workspaceId: string,
|
|
106
|
-
): Promise<boolean> {
|
|
107
|
-
const row = await prisma.spoke.findFirst({
|
|
108
|
-
where: { workspaceId, revokedAt: null },
|
|
109
|
-
select: { id: true },
|
|
110
|
-
});
|
|
111
|
-
return Boolean(row);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ── Operator functions — `bin/spokes.ts` only ────────────────────────────────
|
|
115
|
-
|
|
116
|
-
export interface SpokeListRow {
|
|
117
|
-
id: string;
|
|
118
|
-
name: string;
|
|
119
|
-
workspaceId: string;
|
|
120
|
-
credentialPrefix: string;
|
|
121
|
-
createdAt: Date;
|
|
122
|
-
revokedAt: Date | null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const SPOKE_LIST_SELECT = {
|
|
126
|
-
id: true,
|
|
127
|
-
name: true,
|
|
128
|
-
workspaceId: true,
|
|
129
|
-
credentialPrefix: true,
|
|
130
|
-
createdAt: true,
|
|
131
|
-
revokedAt: true,
|
|
132
|
-
} as const;
|
|
133
|
-
|
|
134
|
-
export type RegisterSpokeResult =
|
|
135
|
-
| { ok: true; spoke: SpokeListRow; key: string }
|
|
136
|
-
| { ok: false; reason: "workspace-not-found" };
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Register a spoke bound to one existing workspace. The returned `key` is the
|
|
140
|
-
* only copy that will ever exist: the caller prints it once.
|
|
141
|
-
*/
|
|
142
|
-
export async function registerSpoke(params: {
|
|
143
|
-
workspaceId: string;
|
|
144
|
-
name: string;
|
|
145
|
-
}): Promise<RegisterSpokeResult> {
|
|
146
|
-
const workspace = await prisma.workspace.findUnique({
|
|
147
|
-
where: { id: params.workspaceId },
|
|
148
|
-
select: { id: true },
|
|
149
|
-
});
|
|
150
|
-
if (!workspace) return { ok: false, reason: "workspace-not-found" };
|
|
151
|
-
|
|
152
|
-
const key = mintSpokeKey();
|
|
153
|
-
const spoke = await prisma.spoke.create({
|
|
154
|
-
data: {
|
|
155
|
-
workspaceId: params.workspaceId,
|
|
156
|
-
name: params.name,
|
|
157
|
-
credentialHash: hashSpokeKey(key),
|
|
158
|
-
credentialPrefix: key.slice(0, CREDENTIAL_PREFIX_LENGTH),
|
|
159
|
-
},
|
|
160
|
-
select: SPOKE_LIST_SELECT,
|
|
161
|
-
});
|
|
162
|
-
return { ok: true, spoke, key };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Revoke one spoke. `false` when no unrevoked spoke has that id. */
|
|
166
|
-
export async function revokeSpoke(id: string): Promise<boolean> {
|
|
167
|
-
const { count } = await prisma.spoke.updateMany({
|
|
168
|
-
where: { id, revokedAt: null },
|
|
169
|
-
data: { revokedAt: new Date() },
|
|
170
|
-
});
|
|
171
|
-
return count === 1;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Every spoke, newest first. Never selects the credential digest. */
|
|
175
|
-
export async function listSpokes(): Promise<SpokeListRow[]> {
|
|
176
|
-
return prisma.spoke.findMany({
|
|
177
|
-
select: SPOKE_LIST_SELECT,
|
|
178
|
-
orderBy: { createdAt: "desc" },
|
|
179
|
-
});
|
|
180
|
-
}
|