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,96 +0,0 @@
|
|
|
1
|
-
// oss: false
|
|
2
|
-
/**
|
|
3
|
-
* The magnet-deliverable Asset store's data-access boundary (#6310 D16).
|
|
4
|
-
*
|
|
5
|
-
* `deliverable-fulfillment.ts` resolves `deliverables.assets[].assetKey`
|
|
6
|
-
* against the workspace-scoped `Asset` model before falling back to the
|
|
7
|
-
* legacy `DeliverableAsset` store (D1/D2), and the download route
|
|
8
|
-
* (`routes/deliverables/index.ts`) serves an `asset_`-class grant's bytes
|
|
9
|
-
* from the same model (D6). Neither caller may run a raw `prisma.asset`
|
|
10
|
-
* query directly — that access is banned by the tenant-scope
|
|
11
|
-
* `forbidden_pattern` in architecture.yaml — and neither is `lib/asset-store.ts`
|
|
12
|
-
* a fit: it is `workspace`/`multi-tenant`-owned (`MODULE_SLICES`), so a
|
|
13
|
-
* `signal-collectors` file (the route) cannot import it, and
|
|
14
|
-
* `deliverable-fulfillment.ts` ships in every scaffold regardless of module
|
|
15
|
-
* selection. This file is its own named boundary instead — modelled
|
|
16
|
-
* line-for-line on `lib/asset-store.ts` — exporting exactly two scoped
|
|
17
|
-
* reads and nothing else:
|
|
18
|
-
*
|
|
19
|
-
* - `findDeliverableAssetIdByKey(workspaceId, key)` — the mint-time
|
|
20
|
-
* resolution (D2): does an `Asset` exist at this workspace's `key`?
|
|
21
|
-
* - `readDeliverableAsset(assetId, workspaceId)` — the serve-time read
|
|
22
|
-
* (D6): the bytes, scoped by the `workspaceId` the grant carries
|
|
23
|
-
* (D15), so a rewritten or mis-set grant can never serve another
|
|
24
|
-
* workspace's bytes.
|
|
25
|
-
*
|
|
26
|
-
* Neither ever resolves a client file (SEC-7877-A, #7877): an asset tagged
|
|
27
|
-
* `private`, one a member-area message attaches, or one a delivered piece
|
|
28
|
-
* version names (#8171 D3), is the same `null` a miss is — so a magnet pointed at a client file's key mints nothing and a
|
|
29
|
-
* grant minted before the file became one serves nothing.
|
|
30
|
-
*
|
|
31
|
-
* No write, no `findMany`, no list, no unscoped read — both functions take
|
|
32
|
-
* a `workspaceId` the caller resolved server-side, never a client-supplied
|
|
33
|
-
* value, so neither can enumerate or cross a tenant boundary (NFR3).
|
|
34
|
-
*/
|
|
35
|
-
import { PRIVATE_ASSET_TAG } from "@working-theory/validation";
|
|
36
|
-
|
|
37
|
-
import { prisma } from "./prisma.js";
|
|
38
|
-
|
|
39
|
-
/** A client file (SEC-7877-A) — `private`-tagged, attached to a message, or a version's file (#8171 D3). */
|
|
40
|
-
async function isClientFile(
|
|
41
|
-
assetId: string,
|
|
42
|
-
workspaceId: string,
|
|
43
|
-
tags: string[],
|
|
44
|
-
): Promise<boolean> {
|
|
45
|
-
if (tags.includes(PRIVATE_ASSET_TAG)) return true;
|
|
46
|
-
if ((await prisma.interaction.count({ where: { assetId, workspaceId } })) > 0) return true;
|
|
47
|
-
return (await prisma.pieceVersion.count({ where: { assetId, workspaceId } })) > 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** What the mint-time resolution needs, and nothing more. */
|
|
51
|
-
export async function findDeliverableAssetIdByKey(
|
|
52
|
-
workspaceId: string,
|
|
53
|
-
key: string,
|
|
54
|
-
): Promise<string | null> {
|
|
55
|
-
const row = await prisma.asset.findFirst({
|
|
56
|
-
where: { workspaceId, key },
|
|
57
|
-
select: { id: true, tags: true },
|
|
58
|
-
});
|
|
59
|
-
if (!row || (await isClientFile(row.id, workspaceId, row.tags))) return null;
|
|
60
|
-
return row.id;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** What the serve path needs, and deliberately nothing more (no key, no workspace). */
|
|
64
|
-
export type DeliverableAssetBytes = {
|
|
65
|
-
filename: string;
|
|
66
|
-
contentType: string;
|
|
67
|
-
bytes: Buffer;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Resolve one asset's bytes by id, scoped by `workspaceId` (D15). A foreign-
|
|
72
|
-
* workspace id — or any other miss — returns `null`; the caller turns that
|
|
73
|
-
* into the same uniform 404 an unknown grant gets (D6/FR5).
|
|
74
|
-
*/
|
|
75
|
-
export async function readDeliverableAsset(
|
|
76
|
-
assetId: string,
|
|
77
|
-
workspaceId: string | null,
|
|
78
|
-
): Promise<DeliverableAssetBytes | null> {
|
|
79
|
-
if (workspaceId === null) return null;
|
|
80
|
-
|
|
81
|
-
const row = await prisma.asset.findFirst({
|
|
82
|
-
where: { id: assetId, workspaceId },
|
|
83
|
-
select: { filename: true, contentType: true, bytes: true, tags: true },
|
|
84
|
-
});
|
|
85
|
-
if (!row || (await isClientFile(assetId, workspaceId, row.tags))) return null;
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
filename: row.filename,
|
|
89
|
-
contentType: row.contentType,
|
|
90
|
-
// Prisma maps a `Bytes` column to `Uint8Array`, and Fastify treats only
|
|
91
|
-
// a `Buffer` as a raw payload — this wraps rather than copies
|
|
92
|
-
// (`Buffer.from(buffer, byteOffset, length)` is a view), mirroring
|
|
93
|
-
// `asset-store.ts`'s own identical wrap.
|
|
94
|
-
bytes: Buffer.from(row.bytes.buffer, row.bytes.byteOffset, row.bytes.byteLength),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fastify error-serializer — prevents internal error detail from leaking to clients.
|
|
3
|
-
*
|
|
4
|
-
* Fastify's default error handler echoes `err.message` (and in dev,
|
|
5
|
-
* `err.stack`) in the response body. For internal errors (DB failures, SDK
|
|
6
|
-
* exceptions, unhandled throws) that's sensitive: connection strings, host
|
|
7
|
-
* names, schema hints, internal paths. This handler:
|
|
8
|
-
*
|
|
9
|
-
* - Maps any 5xx-class internal Error to `{ error: "internal_error" }`
|
|
10
|
-
* with no detail
|
|
11
|
-
* - Logs the full Error (message, stack, request context) to pino at
|
|
12
|
-
* error level
|
|
13
|
-
* - Lets Fastify's built-in 4xx validation errors flow through
|
|
14
|
-
* (their `validation` payload is safe to surface — it describes the
|
|
15
|
-
* client's input, not server internals)
|
|
16
|
-
* - Leaves replies that handlers already committed (explicit status + body)
|
|
17
|
-
* untouched
|
|
18
|
-
*/
|
|
19
|
-
import { idRegex } from "@working-theory/ids";
|
|
20
|
-
import type { FastifyError, FastifyInstance } from "fastify";
|
|
21
|
-
|
|
22
|
-
const GENERIC_INTERNAL_BODY = { error: "internal_error" } as const;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* The request target with its QUERY STRING removed (#5970 security review,
|
|
26
|
-
* finding 1).
|
|
27
|
-
*
|
|
28
|
-
* A route may carry its credential in the query — `GET
|
|
29
|
-
* /v1/requests/:id/confirm?token=…` does, because a link in an email has
|
|
30
|
-
* nowhere else to put one — and this handler logs `path` on EVERY error on
|
|
31
|
-
* EVERY route. Logging the raw target would write that credential into the
|
|
32
|
-
* API's own log the moment anything went wrong, which is precisely when logs
|
|
33
|
-
* get read, copied into tickets, and shipped to third parties.
|
|
34
|
-
*/
|
|
35
|
-
function pathWithoutQuery(url: string): string {
|
|
36
|
-
const queryAt = url.indexOf("?");
|
|
37
|
-
return queryAt === -1 ? url : url.slice(0, queryAt);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* #6084 D14 — `GET /v1/checkout/sessions/:id` puts its credential in the
|
|
42
|
-
* PATH, so a `cs_…`-shaped segment is truncated to its first 8 characters
|
|
43
|
-
* plus `…` (a path SEGMENT rule, never a substring replace, so it cannot
|
|
44
|
-
* corrupt an unrelated URL). #6216 D11 adds the same arm for `lead_…`
|
|
45
|
-
* segments and #8438 SEC-8438-10 for `person_…` segments, which would
|
|
46
|
-
* otherwise put a full id beside `remoteAddress`.
|
|
47
|
-
*/
|
|
48
|
-
const SESSION_ID_LOG_SEGMENT = /^cs_[A-Za-z0-9_]+$/;
|
|
49
|
-
const LEAD_ID_LOG_SEGMENT = idRegex("lead");
|
|
50
|
-
const PERSON_ID_LOG_SEGMENT = idRegex("person");
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* #8932 D10 (contract W9) — a webhook connection token is the per-connection
|
|
54
|
-
* receiver's routing credential (`/v1/webhooks/<provider>/:connectionToken`).
|
|
55
|
-
* Every segment after the provider is replaced by one literal
|
|
56
|
-
* `:connectionToken`, keeping no prefix — unlike the id arms above — and
|
|
57
|
-
* empty segments are dropped first, so a trailing slash, a double slash or a
|
|
58
|
-
* mis-pasted extra segment cannot carry the token past the rule.
|
|
59
|
-
*/
|
|
60
|
-
const CONNECTION_TOKEN_PLACEHOLDER = ":connectionToken";
|
|
61
|
-
|
|
62
|
-
/** A segment as the router matches it: percent-decoded, or raw when it does not decode. */
|
|
63
|
-
function decodedSegment(segment: string): string {
|
|
64
|
-
try {
|
|
65
|
-
return decodeURIComponent(segment);
|
|
66
|
-
} catch {
|
|
67
|
-
return segment;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function redactConnectionToken(path: string): string | null {
|
|
72
|
-
// Compared DECODED: the router decodes before it matches, so
|
|
73
|
-
// `/v1/%77ebhooks/stripe/<token>` reaches the token route and must be
|
|
74
|
-
// redacted like its plain spelling.
|
|
75
|
-
const segments = path
|
|
76
|
-
.split("/")
|
|
77
|
-
.filter((segment) => segment.length > 0)
|
|
78
|
-
.map(decodedSegment);
|
|
79
|
-
if (segments.length < 4 || segments[0] !== "v1" || segments[1] !== "webhooks") {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
return `/${segments.slice(0, 3).join("/")}/${CONNECTION_TOKEN_PLACEHOLDER}`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The ONE path redactor for a log line (#8932 D10): the query string dropped,
|
|
87
|
-
* a webhook connection token replaced whole, and every `cs_…`, `lead_…` and
|
|
88
|
-
* `person_…` segment truncated. The Fastify request serializer (`server.ts`)
|
|
89
|
-
* and this handler's three `path:` fields both call it, so a 429, 413, 415 or
|
|
90
|
-
* 5xx on a token route logs the same redacted path its request line does.
|
|
91
|
-
* A route parameter that is an identifier rather than a secret — `:id` — is
|
|
92
|
-
* kept: it is what makes the line useful.
|
|
93
|
-
*/
|
|
94
|
-
export function redactLogPath(url: string): string {
|
|
95
|
-
const path = pathWithoutQuery(url);
|
|
96
|
-
const tokenless = redactConnectionToken(path);
|
|
97
|
-
if (tokenless !== null) return tokenless;
|
|
98
|
-
return path
|
|
99
|
-
.split("/")
|
|
100
|
-
.map((segment) =>
|
|
101
|
-
SESSION_ID_LOG_SEGMENT.test(segment) ||
|
|
102
|
-
LEAD_ID_LOG_SEGMENT.test(segment) ||
|
|
103
|
-
PERSON_ID_LOG_SEGMENT.test(segment)
|
|
104
|
-
? `${segment.slice(0, 8)}…`
|
|
105
|
-
: segment,
|
|
106
|
-
)
|
|
107
|
-
.join("/");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function registerErrorSerializer(server: FastifyInstance): void {
|
|
111
|
-
server.setErrorHandler(async (err: FastifyError, request, reply) => {
|
|
112
|
-
// Validation errors (Fastify's schema validator) — keep structured detail.
|
|
113
|
-
// These describe the client request, not server internals.
|
|
114
|
-
if (err.validation) {
|
|
115
|
-
const status = err.statusCode ?? 400;
|
|
116
|
-
request.log.warn(
|
|
117
|
-
{
|
|
118
|
-
method: request.method,
|
|
119
|
-
path: redactLogPath(request.url),
|
|
120
|
-
validation: err.validation,
|
|
121
|
-
},
|
|
122
|
-
"request validation failed",
|
|
123
|
-
);
|
|
124
|
-
return reply.code(status).send({
|
|
125
|
-
error: "validation-error",
|
|
126
|
-
details: err.validation,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Explicit 4xx errors thrown by handlers (e.g. fastify.httpErrors.notFound)
|
|
131
|
-
// also flow through — they're a deliberate boundary signal, not an
|
|
132
|
-
// internal leak.
|
|
133
|
-
const status = err.statusCode ?? 500;
|
|
134
|
-
if (status >= 400 && status < 500) {
|
|
135
|
-
request.log.info(
|
|
136
|
-
{
|
|
137
|
-
method: request.method,
|
|
138
|
-
path: redactLogPath(request.url),
|
|
139
|
-
statusCode: status,
|
|
140
|
-
errName: err.name,
|
|
141
|
-
},
|
|
142
|
-
"request returned 4xx",
|
|
143
|
-
);
|
|
144
|
-
// Don't echo err.message — handlers that want a structured body should
|
|
145
|
-
// call reply.code().send() directly. This branch is the safe fallback.
|
|
146
|
-
return reply.code(status).send({ error: err.code ?? "client_error" });
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 5xx — log everything, return nothing identifying.
|
|
150
|
-
request.log.error(
|
|
151
|
-
{
|
|
152
|
-
method: request.method,
|
|
153
|
-
path: redactLogPath(request.url),
|
|
154
|
-
errName: err.name,
|
|
155
|
-
errMessage: err.message,
|
|
156
|
-
errStack: err.stack,
|
|
157
|
-
errCode: err.code,
|
|
158
|
-
},
|
|
159
|
-
"internal server error",
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return reply.code(500).send(GENERIC_INTERNAL_BODY);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical error shape for apps/api content-write routes (D14a Stage 0).
|
|
3
|
-
*
|
|
4
|
-
* Every error response from create/update/delete content routes returns this
|
|
5
|
-
* shape as the JSON body, paired with the appropriate HTTP status. The MCP
|
|
6
|
-
* twin (`apps/mcp/src/lib/errors.ts`) returns the same shape sans `status` —
|
|
7
|
-
* MCP carries the error in the tool result body itself.
|
|
8
|
-
*
|
|
9
|
-
* Codes are a closed set; consumers can switch on them to drive UI / retry
|
|
10
|
-
* logic without parsing free-form `message` text. New codes get added here
|
|
11
|
-
* before any route uses them.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* const err = validationError("Invalid type or slug", { field: "type" });
|
|
15
|
-
* return reply.status(err.status).send(err.body);
|
|
16
|
-
*/
|
|
17
|
-
import { mapAdapterError } from "@working-theory/content-adapters";
|
|
18
|
-
|
|
19
|
-
export type ErrorCode =
|
|
20
|
-
| "validation_failed"
|
|
21
|
-
| "not_found"
|
|
22
|
-
| "conflict"
|
|
23
|
-
| "payment_required"
|
|
24
|
-
| "unauthorized";
|
|
25
|
-
|
|
26
|
-
export interface ErrorBody {
|
|
27
|
-
error: ErrorCode;
|
|
28
|
-
message: string;
|
|
29
|
-
details?: Record<string, unknown>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface ErrorResponse {
|
|
33
|
-
status: number;
|
|
34
|
-
body: ErrorBody;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function makeBody(
|
|
38
|
-
code: ErrorCode,
|
|
39
|
-
message: string,
|
|
40
|
-
details?: Record<string, unknown>,
|
|
41
|
-
): ErrorBody {
|
|
42
|
-
if (details === undefined) {
|
|
43
|
-
return { error: code, message };
|
|
44
|
-
}
|
|
45
|
-
return { error: code, message, details };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function validationError(
|
|
49
|
-
message: string,
|
|
50
|
-
details?: Record<string, unknown>,
|
|
51
|
-
): ErrorResponse {
|
|
52
|
-
return { status: 400, body: makeBody("validation_failed", message, details) };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function unauthorizedError(message: string): ErrorResponse {
|
|
56
|
-
return { status: 401, body: makeBody("unauthorized", message) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function paymentRequiredError(message: string): ErrorResponse {
|
|
60
|
-
return { status: 402, body: makeBody("payment_required", message) };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function notFoundError(message: string): ErrorResponse {
|
|
64
|
-
return { status: 404, body: makeBody("not_found", message) };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function conflictError(
|
|
68
|
-
message: string,
|
|
69
|
-
details?: Record<string, unknown>,
|
|
70
|
-
): ErrorResponse {
|
|
71
|
-
return { status: 409, body: makeBody("conflict", message, details) };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Route-layer adapter for `mapAdapterError` from `@working-theory/content-adapters`.
|
|
76
|
-
* The package emits a canonical `{error, message, details?}` body but
|
|
77
|
-
* cannot know the HTTP status (it has no app dependency). This helper
|
|
78
|
-
* pairs the body with the right status. Returns `null` if the error
|
|
79
|
-
* wasn't an `AdapterWriteError` (caller should treat as 500).
|
|
80
|
-
*/
|
|
81
|
-
export function adapterErrorResponse(
|
|
82
|
-
err: unknown,
|
|
83
|
-
ctx: { type: string; slug: string },
|
|
84
|
-
): ErrorResponse | null {
|
|
85
|
-
const body = mapAdapterError(err, ctx);
|
|
86
|
-
if (body === null) return null;
|
|
87
|
-
switch (body.error) {
|
|
88
|
-
case "conflict":
|
|
89
|
-
return { status: 409, body };
|
|
90
|
-
case "not_found":
|
|
91
|
-
return { status: 404, body };
|
|
92
|
-
case "validation_failed":
|
|
93
|
-
return { status: 400, body };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File-fallback writer for the AILK inbox.
|
|
3
|
-
*
|
|
4
|
-
* Writes a Lead to ${AILK_INBOX_FALLBACK_DIR ?? '.ailk/inbox'}/<ISO-ts>-<lead.id>.json
|
|
5
|
-
* when the database is unavailable. Engaged by B6's lead-routing service on DB failure.
|
|
6
|
-
*
|
|
7
|
-
* Path-traversal-safe by construction:
|
|
8
|
-
* 1. lead.id is validated against /^[a-z0-9_-]{8,64}$/i before any fs operation.
|
|
9
|
-
* 2. The resolved write path is checked to be strictly inside targetDir.
|
|
10
|
-
*
|
|
11
|
-
* No quota / count cap. Operators running with this fallback engaged for extended periods
|
|
12
|
-
* should monitor disk usage and replay leads back to DB once it returns.
|
|
13
|
-
*
|
|
14
|
-
* Module is import-side-effect-free. No env reads at module-load time.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import * as fs from "fs/promises";
|
|
18
|
-
import * as path from "path";
|
|
19
|
-
|
|
20
|
-
import type { Lead } from "@working-theory/validation";
|
|
21
|
-
|
|
22
|
-
export type FallbackLead = Lead;
|
|
23
|
-
|
|
24
|
-
export type FallbackResult =
|
|
25
|
-
| { ok: true; path: string }
|
|
26
|
-
| {
|
|
27
|
-
ok: false;
|
|
28
|
-
error: {
|
|
29
|
-
kind: "invalid_id" | "path_escape" | "fs_error";
|
|
30
|
-
message: string;
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const LEAD_ID_RE = /^[a-z0-9_-]{8,64}$/i;
|
|
35
|
-
|
|
36
|
-
export async function writeLeadFallback(
|
|
37
|
-
lead: FallbackLead,
|
|
38
|
-
): Promise<FallbackResult> {
|
|
39
|
-
if (!LEAD_ID_RE.test(lead.id)) {
|
|
40
|
-
return {
|
|
41
|
-
ok: false,
|
|
42
|
-
error: { kind: "invalid_id", message: `Invalid lead id: ${lead.id}` },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Resolve target directory — read env inside the function (no module-level side effects)
|
|
47
|
-
const envDir = process.env.AILK_INBOX_FALLBACK_DIR ?? ".ailk/inbox";
|
|
48
|
-
const targetDir = path.resolve(process.cwd(), envDir);
|
|
49
|
-
|
|
50
|
-
const rawTs = isValidIso(lead.createdAt)
|
|
51
|
-
? lead.createdAt
|
|
52
|
-
: new Date().toISOString();
|
|
53
|
-
// Replace colons with hyphens for Windows filesystem compatibility
|
|
54
|
-
const ts = rawTs.replace(/:/g, "-");
|
|
55
|
-
|
|
56
|
-
const filename = `${ts}-${lead.id}.json`;
|
|
57
|
-
const resolved = path.resolve(targetDir, filename);
|
|
58
|
-
|
|
59
|
-
// Path-escape check: relative path must not start with '..' or be absolute
|
|
60
|
-
const rel = path.relative(targetDir, resolved);
|
|
61
|
-
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
62
|
-
return {
|
|
63
|
-
ok: false,
|
|
64
|
-
error: {
|
|
65
|
-
kind: "path_escape",
|
|
66
|
-
message: `Path escape detected: ${resolved}`,
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
await fs.mkdir(targetDir, { recursive: true });
|
|
73
|
-
await fs.writeFile(resolved, JSON.stringify(lead, null, 2));
|
|
74
|
-
return { ok: true, path: resolved };
|
|
75
|
-
} catch (err) {
|
|
76
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
77
|
-
return { ok: false, error: { kind: "fs_error", message } };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Require YYYY-MM-DD prefix before accepting as ISO — rejects slash-dates, traversal strings, etc.
|
|
82
|
-
const ISO_RE = /^\d{4}-\d{2}-\d{2}(T[\d:.Z+-]*)?$/;
|
|
83
|
-
|
|
84
|
-
function isValidIso(value: string): boolean {
|
|
85
|
-
if (!value || !ISO_RE.test(value)) return false;
|
|
86
|
-
return !isNaN(new Date(value).getTime());
|
|
87
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A magnet's completion setting — its data boundary (#8757, spec
|
|
3
|
-
* docs/specs/8757-a-per-magnet-completion-setting-spec.md D3).
|
|
4
|
-
*
|
|
5
|
-
* `flow_definitions.completion` / `checkout_offering` are new columns, and
|
|
6
|
-
* `lib/tenant-db.ts` — the magnet routes' only other data path — stays closed
|
|
7
|
-
* under the parallel contract with #8746 (D7), so the two columns get this
|
|
8
|
-
* named boundary of their own. Every statement is scoped by a server-resolved
|
|
9
|
-
* `workspaceId` (a tenant's, or the public lead route's resolved workspace):
|
|
10
|
-
*
|
|
11
|
-
* - `completionsFor` — one `findMany` by workspace AND id list;
|
|
12
|
-
* - `setCompletion` — one `updateMany` by id AND workspace, writing
|
|
13
|
-
* exactly the two columns;
|
|
14
|
-
* - `liveCompletionBySlug` — one `findFirst` of a LIVE magnet by workspace
|
|
15
|
-
* AND slug (its pair, title and config — the
|
|
16
|
-
* config for a declared `completionEmail`);
|
|
17
|
-
* - `inviteSendCounts` — two `lead.count`s for the invite email's caps
|
|
18
|
-
* (D6), by workspace AND `flow:<slug>` source.
|
|
19
|
-
*
|
|
20
|
-
* No list without an id set, no other model, no read by any other key.
|
|
21
|
-
* Base tree (SEC-8757-D): no `oss: false` marker and no `MODULE_SLICES`
|
|
22
|
-
* entry, because the never-gate lead route imports it. `architecture.yaml`
|
|
23
|
-
* lists this file under the tenant-scope rule's `paths_scoped` with exactly
|
|
24
|
-
* these four call sites' counts, so a further access fails the lint.
|
|
25
|
-
*/
|
|
26
|
-
import {
|
|
27
|
-
FLOW_COMPLETION_DEFAULT,
|
|
28
|
-
flowCompletionSettingSchema,
|
|
29
|
-
type FlowCompletionSetting,
|
|
30
|
-
} from "@working-theory/validation";
|
|
31
|
-
|
|
32
|
-
import { prisma } from "./prisma.js";
|
|
33
|
-
|
|
34
|
-
const DEFAULT_SETTING: FlowCompletionSetting = {
|
|
35
|
-
completion: FLOW_COMPLETION_DEFAULT,
|
|
36
|
-
checkoutOffering: null,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* A stored pair as the vocabulary reads it. The database's CHECKs make every
|
|
41
|
-
* stored pair valid; a row that carries neither column (a test fixture, or a
|
|
42
|
-
* read before the migration) reads as the default, as `customersEnabled` does.
|
|
43
|
-
*/
|
|
44
|
-
function toSetting(row: { completion?: unknown; checkoutOffering?: unknown }) {
|
|
45
|
-
const parsed = flowCompletionSettingSchema.safeParse({
|
|
46
|
-
completion: row.completion,
|
|
47
|
-
checkoutOffering: row.checkoutOffering ?? null,
|
|
48
|
-
});
|
|
49
|
-
return parsed.success ? parsed.data : DEFAULT_SETTING;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** The setting of each of `ids` in `workspaceId`. An id absent from the map reads as `lead`. */
|
|
53
|
-
export async function completionsFor(
|
|
54
|
-
workspaceId: string,
|
|
55
|
-
ids: readonly string[],
|
|
56
|
-
): Promise<Map<string, FlowCompletionSetting>> {
|
|
57
|
-
const out = new Map<string, FlowCompletionSetting>();
|
|
58
|
-
if (ids.length === 0) return out;
|
|
59
|
-
const rows = await prisma.flowDefinition.findMany({
|
|
60
|
-
where: { workspaceId, id: { in: [...ids] } },
|
|
61
|
-
select: { id: true, completion: true, checkoutOffering: true },
|
|
62
|
-
});
|
|
63
|
-
for (const row of rows) out.set(row.id, toSetting(row));
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** `completionsFor`'s answer for one id. */
|
|
68
|
-
export function completionOf(
|
|
69
|
-
settings: Map<string, FlowCompletionSetting>,
|
|
70
|
-
id: string,
|
|
71
|
-
): FlowCompletionSetting {
|
|
72
|
-
return settings.get(id) ?? DEFAULT_SETTING;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Write the pair onto magnet `id` of `workspaceId`; nothing else on the row. */
|
|
76
|
-
export async function setCompletion(
|
|
77
|
-
workspaceId: string,
|
|
78
|
-
id: string,
|
|
79
|
-
setting: FlowCompletionSetting,
|
|
80
|
-
): Promise<"set" | "not_found"> {
|
|
81
|
-
const result = await prisma.flowDefinition.updateMany({
|
|
82
|
-
where: { id, workspaceId },
|
|
83
|
-
data: { completion: setting.completion, checkoutOffering: setting.checkoutOffering },
|
|
84
|
-
});
|
|
85
|
-
return result.count > 0 ? "set" : "not_found";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export type LiveCompletion = FlowCompletionSetting & {
|
|
89
|
-
title: string;
|
|
90
|
-
/** The stored config, unparsed — the invite email reads its `completionEmail`. */
|
|
91
|
-
config: unknown;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/** The LIVE magnet at `slug` in `workspaceId`: its setting, title and config, or `null`. */
|
|
95
|
-
export async function liveCompletionBySlug(
|
|
96
|
-
workspaceId: string,
|
|
97
|
-
slug: string,
|
|
98
|
-
): Promise<LiveCompletion | null> {
|
|
99
|
-
const row = await prisma.flowDefinition.findFirst({
|
|
100
|
-
where: { workspaceId, slug, status: "live" },
|
|
101
|
-
select: { title: true, config: true, completion: true, checkoutOffering: true },
|
|
102
|
-
});
|
|
103
|
-
return row === null ? null : { ...toSetting(row), title: row.title, config: row.config };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
107
|
-
const HOUR_MS = 60 * 60 * 1000;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* D6's two caps, counted in the database so they hold across instances and
|
|
111
|
-
* against a spoofed `x-forwarded-for`: this magnet's leads for `email`
|
|
112
|
-
* (case-insensitively) over the last day, and all of its leads over the last
|
|
113
|
-
* hour. Both include the lead the caller just wrote.
|
|
114
|
-
*/
|
|
115
|
-
export async function inviteSendCounts(
|
|
116
|
-
workspaceId: string,
|
|
117
|
-
slug: string,
|
|
118
|
-
email: string,
|
|
119
|
-
now: Date,
|
|
120
|
-
): Promise<{ recipientDay: number; siteHour: number }> {
|
|
121
|
-
const source = `flow:${slug}`;
|
|
122
|
-
const [recipientDay, siteHour] = await Promise.all([
|
|
123
|
-
prisma.lead.count({
|
|
124
|
-
where: {
|
|
125
|
-
workspaceId,
|
|
126
|
-
source,
|
|
127
|
-
email: { equals: email, mode: "insensitive" },
|
|
128
|
-
createdAt: { gte: new Date(now.getTime() - DAY_MS) },
|
|
129
|
-
},
|
|
130
|
-
}),
|
|
131
|
-
prisma.lead.count({
|
|
132
|
-
where: { workspaceId, source, createdAt: { gte: new Date(now.getTime() - HOUR_MS) } },
|
|
133
|
-
}),
|
|
134
|
-
]);
|
|
135
|
-
return { recipientDay, siteHour };
|
|
136
|
-
}
|