create-ailk 0.1.1 → 0.3.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/component-catalog.md +210 -52
- package/dist/cli.js +0 -0
- package/dist/lib/apply-module-patches.d.ts +47 -0
- package/dist/lib/apply-module-patches.js +370 -0
- package/dist/lib/extract-module-bundle.d.ts +12 -3
- package/dist/lib/extract-module-bundle.js +236 -24
- package/dist/lib/fetch-module.d.ts +3 -38
- package/dist/lib/fetch-module.js +26 -8
- package/dist/lib/module-license-gate.d.ts +2 -0
- package/dist/lib/module-license-gate.js +1 -0
- package/dist/lib/paid-paths.d.ts +16 -0
- package/dist/lib/paid-paths.js +90 -0
- package/dist/module-architecture.d.ts +139 -23
- package/dist/module-architecture.js +668 -25
- package/dist/parse-args.d.ts +2 -2
- package/dist/parse-args.js +3 -1
- package/dist/programmatic.d.ts +2 -1
- package/dist/programmatic.js +15 -4
- package/dist/surfaces.d.ts +12 -36
- package/dist/surfaces.js +35 -5
- package/dist/sync-routes.js +31 -1
- package/package.json +14 -15
- package/templates/.claude/agents/web.md +2 -3
- package/templates/.claude/rules/architecture.md +5 -5
- package/templates/.claude/skills/README.md +2 -1
- package/templates/.env.example +21 -9
- package/templates/CONVENTIONS.md +6 -7
- package/templates/apps/api/.env.example +9 -0
- package/templates/apps/api/CLAUDE.md +35 -4
- package/templates/apps/api/package.json +0 -1
- package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
- package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
- package/templates/apps/api/src/config/index.ts +19 -0
- package/templates/apps/api/src/config/modules.ts +12 -14
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +13 -0
- package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
- package/templates/apps/api/src/lib/slice-load.ts +90 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
- package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
- package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
- package/templates/apps/api/src/openapi/index.ts +9 -3
- package/templates/apps/api/src/openapi/spec.ts +505 -65
- package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
- package/templates/apps/api/src/routes/content/index.ts +38 -11
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +28 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +144 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +478 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +717 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +35 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +484 -0
- package/templates/apps/api/src/routes/project-listings/index.ts +52 -10
- package/templates/apps/api/src/routes/project-listings/me.ts +86 -0
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +38 -0
- package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
- package/templates/apps/api/src/server.ts +471 -133
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
- package/templates/apps/api/src/services/consent.ts +236 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
- package/templates/apps/api/src/services/listing-config.ts +58 -0
- package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
- package/templates/apps/api/src/services/project-listings.ts +602 -29
- package/templates/apps/api/src/vercel-handler.ts +60 -26
- package/templates/apps/mcp/.env.example +8 -0
- package/templates/apps/mcp/CLAUDE.md +2 -2
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
- package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
- package/templates/apps/mcp/src/config/modules.ts +14 -13
- package/templates/apps/mcp/src/server.ts +8 -4
- package/templates/apps/mcp/src/tools/index.ts +7 -22
- package/templates/apps/web/.env.example +15 -0
- package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/layout.tsx +13 -1
- package/templates/apps/web/app/llms.txt/route.ts +12 -9
- package/templates/apps/web/jest.config.cjs +20 -13
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
- package/templates/apps/web/lib/site-brand.tsx +4 -1
- package/templates/apps/web/lib/site-theme.ts +74 -0
- package/templates/apps/web/next.config.mjs +1 -3
- package/templates/apps/web/package.json +1 -6
- package/templates/apps/web/public/android-chrome-192x192.png +0 -0
- package/templates/apps/web/public/android-chrome-512x512.png +0 -0
- package/templates/apps/web/public/apple-touch-icon.png +0 -0
- package/templates/apps/web/public/favicon-16x16.png +0 -0
- package/templates/apps/web/public/favicon-32x32.png +0 -0
- package/templates/apps/web/public/favicon.ico +0 -0
- package/templates/apps/web/public/favicon.svg +6 -3
- package/templates/apps/web/public/lockup-horizontal.svg +4 -0
- package/templates/apps/web/public/logomark.svg +4 -0
- package/templates/apps/web/public/site.webmanifest +2 -2
- package/templates/apps/web/public/wordmark.svg +4 -0
- package/templates/content/_site.mdx +12 -0
- package/templates/database/CHANGELOG.md +94 -0
- package/templates/database/inbox/schema.prisma +165 -0
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
- package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
- package/templates/database/package.json +1 -1
- package/templates/database/scripts/db-generate-locked.sh +0 -0
- package/templates/package.json +1 -1
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
- package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
- package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
- package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
- package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
- package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
- package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
- package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
- package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
- package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
- package/templates/apps/api/src/__tests__/server.test.ts +0 -253
- package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
- package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
- package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
- package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
- package/templates/apps/api/src/bin/seed-presets.ts +0 -58
- package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
- package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
- package/templates/apps/api/src/lib/stripe.ts +0 -52
- package/templates/apps/api/src/lib/tenant-db.ts +0 -225
- package/templates/apps/api/src/lib/ws-token.ts +0 -91
- package/templates/apps/api/src/middleware/tenant.ts +0 -99
- package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
- package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
- package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
- package/templates/apps/api/src/routes/billing/index.ts +0 -36
- package/templates/apps/api/src/routes/billing/portal.ts +0 -182
- package/templates/apps/api/src/routes/billing/read.ts +0 -75
- package/templates/apps/api/src/routes/billing/usage.ts +0 -153
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
- package/templates/apps/api/src/routes/checkout/index.ts +0 -13
- package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
- package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
- package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
- package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
- package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
- package/templates/apps/api/src/routes/flows/README.md +0 -147
- package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
- package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
- package/templates/apps/api/src/routes/flows/index.ts +0 -202
- package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
- package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
- package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
- package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
- package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
- package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
- package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
- package/templates/apps/api/src/routes/schedule/index.ts +0 -249
- package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
- package/templates/apps/api/src/routes/slack/actions.ts +0 -177
- package/templates/apps/api/src/routes/slack/index.ts +0 -38
- package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
- package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
- package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
- package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
- package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
- package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
- package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
- package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
- package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
- package/templates/apps/api/src/routes/webhooks/README.md +0 -80
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
- package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
- package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
- package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
- package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
- package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
- package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
- package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
- package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
- package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
- package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
- package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
- package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
- package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
- package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
- package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
- package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
- package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
- package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
- package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
- package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
- package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
- package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
- package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
- package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
- package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
- package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
- package/templates/apps/api/src/services/flow-engine.ts +0 -1278
- package/templates/apps/api/src/services/lead-promotion.ts +0 -176
- package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
- package/templates/apps/api/src/services/playbook-compile.ts +0 -398
- package/templates/apps/api/src/services/playbook-render.ts +0 -263
- package/templates/apps/api/src/services/project-listing-decision.ts +0 -490
- package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
- package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
- package/templates/apps/api/src/services/recommender-capture.ts +0 -835
- package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
- package/templates/apps/api/src/services/scheduling/index.ts +0 -63
- package/templates/apps/api/src/services/scheduling/types.ts +0 -88
- package/templates/apps/api/src/services/tenant-context.ts +0 -451
- package/templates/apps/api/src/services/usage-metering.ts +0 -699
- package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
- package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
- package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
- package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
- package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
- package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
- package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
- package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
- package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
- package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
- package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
- package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
- package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
- package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
- package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
- package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
- package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
- package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
- package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
- package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
- package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
- package/templates/apps/web/content/en/blog/README.txt +0 -25
- package/templates/apps/web/content/en/docs/index.mdx +0 -28
- package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
- package/templates/apps/web/lib/blog-list-data.ts +0 -95
- package/templates/apps/web/lib/blog-post-data.ts +0 -79
- package/templates/apps/web/lib/source.ts +0 -34
- package/templates/apps/web/public/logomark-dark.svg +0 -4
- package/templates/apps/web/public/logomark-light.svg +0 -4
- package/templates/apps/web/source.config.ts +0 -78
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
* `architecture.yaml` template files — a fixed template IS the drift this
|
|
22
22
|
* removes.
|
|
23
23
|
*/
|
|
24
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
25
|
+
import { dirname, join, sep } from "node:path";
|
|
26
|
+
import { PAID_MODULES } from "./lib/module-tier.js";
|
|
24
27
|
import { RECOGNIZED_MODULES } from "./parse-args.js";
|
|
25
28
|
/**
|
|
26
29
|
* The standalone-site "no upstream-monorepo import" forbidden pattern, assembled
|
|
@@ -65,6 +68,19 @@ const PUBLIC_SCHEDULING_VAR_PATTERN = [
|
|
|
65
68
|
"CAL",
|
|
66
69
|
"_|CALCOM|CALENDAR|SCHEDUL|BOOKING)",
|
|
67
70
|
].join("");
|
|
71
|
+
/**
|
|
72
|
+
* The commerce module's one credential-boundary pattern: a live-mode Stripe
|
|
73
|
+
* key shape (`sk_`/`pk_`/`rk_` + the live-mode infix), assembled from
|
|
74
|
+
* fragments for the same reason as CAL_KEY_PATTERN above — a literal
|
|
75
|
+
* live-key-shaped token in this file's source would itself trip the repo's
|
|
76
|
+
* own live-key hard block (`rules/environment-boundaries.md`). `apps/api` is
|
|
77
|
+
* excluded because `STRIPE_SECRET_KEY` legitimately lives there, gated by
|
|
78
|
+
* `assertSandboxKey`'s sandbox-only allow-list at request time and by
|
|
79
|
+
* `assertCommerceSandboxKey` at boot (server.ts) — this pattern catches a
|
|
80
|
+
* live-shaped key leaking anywhere ELSE (a fixture, a client component, a
|
|
81
|
+
* `NEXT_PUBLIC_` var).
|
|
82
|
+
*/
|
|
83
|
+
const STRIPE_LIVE_KEY_PATTERN = ["(sk|pk|rk)_li", "ve_[A-Za-z0-9]"].join("");
|
|
68
84
|
/**
|
|
69
85
|
* The module-independent base: `apps/web` (the Next.js presentation shell every
|
|
70
86
|
* AILK site has) plus the standalone forbidden-pattern floor. `may_import_from`
|
|
@@ -193,8 +209,8 @@ export const MODULE_SLICES = {
|
|
|
193
209
|
},
|
|
194
210
|
},
|
|
195
211
|
},
|
|
196
|
-
// ── docs —
|
|
197
|
-
docs: {
|
|
212
|
+
// ── docs-blog — Pro module: both fumadocs surfaces (D-46) ────────────────
|
|
213
|
+
"docs-blog": {
|
|
198
214
|
webImports: ["packages/fumadocs-schema"],
|
|
199
215
|
packages: {
|
|
200
216
|
"fumadocs-schema": {
|
|
@@ -202,27 +218,157 @@ export const MODULE_SLICES = {
|
|
|
202
218
|
may_import_from: ["packages/schema", "packages/validation"],
|
|
203
219
|
},
|
|
204
220
|
},
|
|
205
|
-
// ownedFiles (S1, #5088).
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
221
|
+
// ownedFiles (S1, #5088; widened by #5365). The standing reason to list
|
|
222
|
+
// only the `/docs` route was that `/blog` was a FREE route, which made
|
|
223
|
+
// `lib/source.ts` and `source.config.ts` genuinely mixed files a strip
|
|
224
|
+
// tool could not touch without breaking every OSS build. D-46 removed
|
|
225
|
+
// that premise: docs and blog are ONE Pro module and Free has no blog, so
|
|
226
|
+
// both files are wholly module-owned and move whole (#5365 D3). Nothing
|
|
227
|
+
// in a Free `apps/web` resolves a Fumadocs collection afterwards, which is
|
|
228
|
+
// also why `apps/web/content/<locale>/{blog,docs}` no longer has to exist
|
|
229
|
+
// in every scaffold — see `programmatic.ts`'s note and #5365 D3.
|
|
230
|
+
//
|
|
231
|
+
// `packages/fumadocs-schema/**` is still deliberately NOT listed. It is a
|
|
232
|
+
// permission, not a physical slice: `webImports` above names it so a
|
|
233
|
+
// scaffold that installs this module declares the import, while the
|
|
234
|
+
// package itself stays in the tree for this repo's own marketing site. Its
|
|
235
|
+
// tier, licence and npm access flip together under #5363 (D-43) — after
|
|
236
|
+
// this extraction, because architecture.yaml's recorded reason for the
|
|
237
|
+
// current `oss` tier is the Free `/blog` route this card just retired.
|
|
238
|
+
//
|
|
239
|
+
// SOURCE files only (the rule above): the three `content/en/**` MDX files
|
|
240
|
+
// and every test/story companion carry the `oss: false` marker but are not
|
|
241
|
+
// listed here.
|
|
242
|
+
ownedFiles: [
|
|
243
|
+
// The two route surfaces the module mounts.
|
|
244
|
+
"apps/web/app/[locale]/blog/[[...slug]]/page.tsx",
|
|
245
|
+
"apps/web/app/[locale]/docs/[[...slug]]/page.tsx",
|
|
246
|
+
// The Fumadocs source pair — both whole, both loaders (D3).
|
|
247
|
+
"apps/web/source.config.ts",
|
|
248
|
+
"apps/web/lib/source.ts",
|
|
249
|
+
// The route adapters and the jest stub for the codegen output.
|
|
250
|
+
"apps/web/lib/blog-list-data.ts",
|
|
251
|
+
"apps/web/lib/blog-post-data.ts",
|
|
252
|
+
"apps/web/lib/__mocks__/source-server.js",
|
|
253
|
+
// The module's own /llms.txt variant; the bundle installs it onto
|
|
254
|
+
// app/llms.txt/route.ts (#5365 D9, install-path mapping owned by #5373).
|
|
255
|
+
"apps/web/lib/slices/docs-blog/llms-route.ts",
|
|
256
|
+
// The two shells and the six components, each off its public barrel and
|
|
257
|
+
// reachable only from the slice barrel that leaves with them (D-45, D5).
|
|
258
|
+
"packages/templates/src/shells/BlogShell.tsx",
|
|
259
|
+
"packages/templates/src/shells/DocsShell.tsx",
|
|
260
|
+
"packages/templates/src/shells/slices/docs-blog.ts",
|
|
261
|
+
"packages/ui/src/blocks/DocsActionBar.tsx",
|
|
262
|
+
"packages/ui/src/blocks/DocsArticleHeader.tsx",
|
|
263
|
+
"packages/ui/src/blocks/DocsFeedback.tsx",
|
|
264
|
+
"packages/ui/src/blocks/DocsSearch.tsx",
|
|
265
|
+
"packages/ui/src/blocks/DocsTocButton.tsx",
|
|
266
|
+
"packages/ui/src/chrome/DocsSidebar.tsx",
|
|
267
|
+
"packages/ui/src/chrome/slices/docs-blog.ts",
|
|
268
|
+
],
|
|
269
|
+
// patches (spec 5418 § 3 D1, Group G) — every Group G row that edits a
|
|
270
|
+
// file the buyer's scaffold already has. Values transcribed verbatim
|
|
271
|
+
// from docs/specs/5365-docs-blog-module-spec.md § 4 Group G via
|
|
272
|
+
// docs/specs/5418-module-bundle-patch-set-spec.md § 3 D1's table; the
|
|
273
|
+
// fumadocs-core/fumadocs-mdx version strings are read from this repo's
|
|
274
|
+
// own apps/web/package.json at HEAD.
|
|
275
|
+
//
|
|
276
|
+
// Group G's tsconfig.json row (D1 row 8 — restoring the
|
|
277
|
+
// `../../packages/fumadocs-schema` project reference) is deliberately
|
|
278
|
+
// NOT included here. Re-anchoring D1 against a real keyless scaffold
|
|
279
|
+
// (5418 build, Deliverable 0) found `apps/web/tsconfig.json` carries no
|
|
280
|
+
// `references` key at all in ANY scaffold, regardless of `--modules`
|
|
281
|
+
// selection: `build-templates.mjs`'s `stripDanglingProjectReferences()`
|
|
282
|
+
// runs once over the fixed templates/ snapshot, which never contains a
|
|
283
|
+
// `packages/` directory, so every one of the monorepo's 15 references
|
|
284
|
+
// (including `fumadocs-schema`, position 6) resolves to a nonexistent
|
|
285
|
+
// path and the whole property is dropped. `@working-theory/fumadocs-schema`
|
|
286
|
+
// is delivered to a scaffold as an npm/bundle dependency under
|
|
287
|
+
// `node_modules/`, never as a `packages/fumadocs-schema` source sibling
|
|
288
|
+
// (see this slice's own `ownedFiles` comment above), so restoring the
|
|
289
|
+
// reference would immediately be dangling again in every scaffold — the
|
|
290
|
+
// exact condition `stripDanglingProjectReferences()` exists to prevent,
|
|
291
|
+
// which breaks `next build`'s TypeScript pass (#4090 item 2). This is
|
|
292
|
+
// the same reasoning the commerce row (5418 D1 row 9) already applies to
|
|
293
|
+
// its own tsconfig.json omission; applied here for consistency.
|
|
211
294
|
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
295
|
+
// Group G's fourth jest.config.cjs item (the `import.meta` transform
|
|
296
|
+
// entry) needs no patch either — the scaffold already ships it
|
|
297
|
+
// untouched (5418 D1 row 7b) — so `add-transform` has no concrete use
|
|
298
|
+
// here.
|
|
299
|
+
patches: [
|
|
300
|
+
{
|
|
301
|
+
target: "apps/web/package.json",
|
|
302
|
+
kind: "json",
|
|
303
|
+
ops: [
|
|
304
|
+
{
|
|
305
|
+
op: "merge-keys",
|
|
306
|
+
path: ["dependencies"],
|
|
307
|
+
entries: {
|
|
308
|
+
"fumadocs-core": "^16.10.4",
|
|
309
|
+
"fumadocs-mdx": "^15.0.12",
|
|
310
|
+
"@working-theory/fumadocs-schema": "workspace:*",
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
op: "merge-keys",
|
|
315
|
+
path: ["scripts"],
|
|
316
|
+
entries: {
|
|
317
|
+
prebuild: "fumadocs-mdx",
|
|
318
|
+
pretypecheck: "fumadocs-mdx",
|
|
319
|
+
pretest: "fumadocs-mdx",
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
target: "apps/web/next.config.mjs",
|
|
326
|
+
kind: "next-config",
|
|
327
|
+
ops: [
|
|
328
|
+
{
|
|
329
|
+
op: "add-transpile-package",
|
|
330
|
+
package: "@working-theory/fumadocs-schema",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
op: "wrap-export-with",
|
|
334
|
+
importLine: 'import { createMDX } from "fumadocs-mdx/next";\n',
|
|
335
|
+
find: "withNextIntl(nextConfig)",
|
|
336
|
+
replaceWith: "withNextIntl(createMDX()(nextConfig))",
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
target: "apps/web/jest.config.cjs",
|
|
342
|
+
kind: "jest-config",
|
|
343
|
+
ops: [
|
|
344
|
+
{
|
|
345
|
+
op: "add-transform-ignore-pattern",
|
|
346
|
+
find: '"/node_modules/(?!(\\\\.pnpm/)?@working-theory(?:\\\\+[^/]*?@?[^/]*?|/[^/]+)(/|$))"',
|
|
347
|
+
replaceWith: '"/node_modules/(?!(\\\\.pnpm/)?(fumadocs-[^/]*?@?[^/]*?|@working-theory(?:\\\\+[^/]*?@?[^/]*?|/[^/]+))(/|$))"',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
op: "add-module-name-mapper",
|
|
351
|
+
pattern: "^@/\\\\.source/server$",
|
|
352
|
+
value: "<rootDir>/lib/__mocks__/source-server.js",
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
op: "add-module-name-mapper",
|
|
356
|
+
pattern: "^@/\\\\.source$",
|
|
357
|
+
value: "<rootDir>/.source/index.ts",
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
op: "add-module-name-mapper",
|
|
361
|
+
pattern: "^@working-theory/fumadocs-schema$",
|
|
362
|
+
value: "<rootDir>/node_modules/@working-theory/fumadocs-schema/dist/index.js",
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
// postInstall shrinks to what patches cannot express (D5): the patch
|
|
368
|
+
// step edits package.json but never runs a package manager on the
|
|
369
|
+
// buyer's behalf, so a fresh pnpm-lock.yaml resolution is still the
|
|
370
|
+
// buyer's own next step.
|
|
371
|
+
postInstall: "run pnpm install",
|
|
226
372
|
},
|
|
227
373
|
// ── multi-tenant — full-stack module (data + api + presentation) ─────────
|
|
228
374
|
//
|
|
@@ -252,7 +398,7 @@ export const MODULE_SLICES = {
|
|
|
252
398
|
},
|
|
253
399
|
api: {
|
|
254
400
|
path: "apps/api",
|
|
255
|
-
description: "Public HTTP API — routes + services (Fastify). Owns webhooks,
|
|
401
|
+
description: "Public HTTP API — routes + services (Fastify). Owns webhooks, email, server-side analytics — checkout moved to the commerce module (D-47).",
|
|
256
402
|
may_import_from: [
|
|
257
403
|
"database",
|
|
258
404
|
"packages/analytics",
|
|
@@ -317,7 +463,7 @@ export const MODULE_SLICES = {
|
|
|
317
463
|
// Object.assign merge in deriveArchitecture is order-independent (a
|
|
318
464
|
// multi-tenant-only selection stays self-consistent).
|
|
319
465
|
templates: {
|
|
320
|
-
description: "Shell/Chrome composition — WorkspaceShell + ArtifactEditorShell (
|
|
466
|
+
description: "Shell/Chrome composition — AppShell (tier-5 shell); WorkspaceShell + ArtifactEditorShell ship inside this module itself, not the public package (D-45)",
|
|
321
467
|
may_import_from: ["packages/theme", "packages/ui", "packages/validation"],
|
|
322
468
|
},
|
|
323
469
|
theme: {
|
|
@@ -435,6 +581,24 @@ export const MODULE_SLICES = {
|
|
|
435
581
|
// not a documented module boundary — flagged for reviewer judgment
|
|
436
582
|
// rather than asserted here.
|
|
437
583
|
//
|
|
584
|
+
// apps/web/app/[locale]/(authed)/waitlist/** (#5367 FR4, D8): the operator
|
|
585
|
+
// waitlist-experiments dashboard UI. Reads GET /v1/waitlist-experiments,
|
|
586
|
+
// whose API half is already listed above. No prior module claimed
|
|
587
|
+
// apps/web at all for multi-tenant — #5182 §8 flagged this gap and left
|
|
588
|
+
// it for this card. Presentation follows its API (D-48: "Operator review
|
|
589
|
+
// dashboards stay in Pro Plus").
|
|
590
|
+
//
|
|
591
|
+
// ESCALATION (§5.3, posted to Epic #5378): the spec's §4.3 file list names
|
|
592
|
+
// 12 files including format.ts and components/ForbiddenState.tsx as
|
|
593
|
+
// multi-tenant-owned. Both are imported by the FREE
|
|
594
|
+
// apps/web/app/[locale]/(authed)/account/page.tsx (lines 55-56) — D-49
|
|
595
|
+
// keeps that page in Free. Listing them here would remove a Free
|
|
596
|
+
// consumer's dependency from the starter (§5.3's stop condition). Treated
|
|
597
|
+
// the same as this slice's own project-listings/index.ts + respond.ts
|
|
598
|
+
// precedent immediately above: a file shared between the free and paid
|
|
599
|
+
// surfaces stays OUT of ownedFiles. Only the other 10 of the 12 are
|
|
600
|
+
// listed below.
|
|
601
|
+
//
|
|
438
602
|
// database/auth/ and database/content/ — investigated, NOT listed.
|
|
439
603
|
// Both namespaces DO have their own schema.prisma file (a real
|
|
440
604
|
// filesystem split via Prisma's multi-file schema feature), but each
|
|
@@ -472,6 +636,16 @@ export const MODULE_SLICES = {
|
|
|
472
636
|
"apps/api/src/services/waitlist-dashboard.ts",
|
|
473
637
|
"apps/api/src/lib/tenant-db.ts",
|
|
474
638
|
"apps/api/src/lib/ws-token.ts",
|
|
639
|
+
"apps/web/app/[locale]/(authed)/waitlist/page.tsx",
|
|
640
|
+
"apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx",
|
|
641
|
+
"apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx",
|
|
642
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx",
|
|
643
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx",
|
|
644
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx",
|
|
645
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx",
|
|
646
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
|
|
647
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
|
|
648
|
+
"apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
|
|
475
649
|
"apps-paid/workspace/**",
|
|
476
650
|
],
|
|
477
651
|
},
|
|
@@ -511,7 +685,7 @@ export const MODULE_SLICES = {
|
|
|
511
685
|
apps: {
|
|
512
686
|
api: {
|
|
513
687
|
path: "apps/api",
|
|
514
|
-
description: "Public HTTP API — routes + services (Fastify). Owns webhooks,
|
|
688
|
+
description: "Public HTTP API — routes + services (Fastify). Owns webhooks, email, server-side analytics — checkout moved to the commerce module (D-47).",
|
|
515
689
|
may_import_from: [
|
|
516
690
|
"database",
|
|
517
691
|
"packages/analytics",
|
|
@@ -643,6 +817,337 @@ export const MODULE_SLICES = {
|
|
|
643
817
|
"apps/web/app/[locale]/schedule/confirmed/page.tsx",
|
|
644
818
|
],
|
|
645
819
|
},
|
|
820
|
+
// ── signal-collectors — flows, deliverables, project listings, waitlist
|
|
821
|
+
// signups (D-48, #5366) ───────────────────────────────────────────────
|
|
822
|
+
//
|
|
823
|
+
// ownedFiles verified against the current filesystem, server.ts's
|
|
824
|
+
// module-conditional registration (isModuleEnabled('signal-collectors')),
|
|
825
|
+
// and a repo-wide grep of every importer of each candidate file (an
|
|
826
|
+
// independent re-grep, not carried over from the spec's own table).
|
|
827
|
+
//
|
|
828
|
+
// apps/api/src/routes/project-listings/index.ts is MIXED (D4): it holds
|
|
829
|
+
// this module's five anonymous/founder-read re-exports (lines 1-55) AND
|
|
830
|
+
// multi-tenant's TENANT_HANDLERS table + projectListingTenantRoutes
|
|
831
|
+
// (lines 57-139) in one file. NOT listed here — never deleted/edited on
|
|
832
|
+
// its own, exactly like the scheduling/multi-tenant precedent for a mixed
|
|
833
|
+
// barrel above.
|
|
834
|
+
//
|
|
835
|
+
// apps/api/src/routes/project-listings/respond.ts (`sendValidated`) IS
|
|
836
|
+
// listed, even though it is ALSO imported directly by the tenant routes
|
|
837
|
+
// get.ts/list.ts/patch.ts/patch-copy.ts (multi-tenant, Pro Plus territory
|
|
838
|
+
// — grep-verified, four direct importers outside this module). Same D6
|
|
839
|
+
// reasoning as the services below: every non-territory consumer is a
|
|
840
|
+
// strictly higher (Pro Plus) level, never Free, so this module owns the
|
|
841
|
+
// file and #5367 records the prerequisite.
|
|
842
|
+
//
|
|
843
|
+
// Ten services below are reached from Pro Plus's project-listings tenant
|
|
844
|
+
// routes (get.ts/list.ts/patch.ts/patch-copy.ts) or commerce's recommender,
|
|
845
|
+
// directly or transitively through project-listing-decision.ts /
|
|
846
|
+
// project-listing-flow.ts — grep-verified, not carried over from the
|
|
847
|
+
// spec's table (which independently reaches the same eight; this pass adds
|
|
848
|
+
// waitlist-scoring.ts and playbook-compile.ts, both of which the finalize
|
|
849
|
+
// scorecard already flagged as under-described). D6's rule: this module
|
|
850
|
+
// owns the shared file because every OTHER consumer is a superset level
|
|
851
|
+
// (OFFERINGS.md §4a Table 1 — Pro Plus contains Pro; the commerce add-on is
|
|
852
|
+
// never sold at Free); #5364/#5367 each record this module as a
|
|
853
|
+
// prerequisite of the routes that reach it rather than re-homing the file.
|
|
854
|
+
// - flow-engine.ts — pro-plus, via project-listing-flow.ts:49 ←
|
|
855
|
+
// project-listing-decision.ts:81 ← patch.ts:38
|
|
856
|
+
// - waitlist-scoring.ts — pro-plus, transitively (sole importer is
|
|
857
|
+
// flow-engine.ts:56, which is itself shared)
|
|
858
|
+
// - playbook-compile.ts — pro-plus, transitively (imported by
|
|
859
|
+
// waitlist-scoring.ts:33 and, in-module, by
|
|
860
|
+
// playbook-render.ts:62 and
|
|
861
|
+
// routes/deliverables/index.ts:44)
|
|
862
|
+
// - project-listings.ts, listing-config.ts, project-listing-decision.ts,
|
|
863
|
+
// project-listing-flow.ts, project-listing-issue.ts,
|
|
864
|
+
// waitlist-experiments.ts — pro-plus, direct or transitive via the
|
|
865
|
+
// decision/flow chain above
|
|
866
|
+
// - deliverable-fulfillment.ts — commerce, via recommender-capture.ts:70.
|
|
867
|
+
// NOT ADDED to ownedFiles below, unlike the rest of this list — spec
|
|
868
|
+
// §5.4's escalation boundary fires for this ONE file: D6's premise is
|
|
869
|
+
// "every OTHER consumer is a superset level, never Free," but
|
|
870
|
+
// recommender-capture.ts (and recommender.ts above it, and server.ts
|
|
871
|
+
// above THAT via a plain static import) is UNCONDITIONAL today — #5364
|
|
872
|
+
// has not landed to gate it behind isModuleEnabled("commerce"). Adding
|
|
873
|
+
// this file to ownedFiles made `paidModuleTemplateExclusions()`'s
|
|
874
|
+
// import-graph closure exclude recommender.ts, then server.ts itself,
|
|
875
|
+
// from every keyless scaffold (empirically found, #5366 resume pass) —
|
|
876
|
+
// a base-breaking regression a template snapshot must never produce.
|
|
877
|
+
// Revert this line once #5364 lands and gates the recommender routes;
|
|
878
|
+
// until then the file ships in every scaffold regardless of module
|
|
879
|
+
// selection, exactly as it did before this card (a narrower, temporary
|
|
880
|
+
// miss than the one D6 accepts on purpose for the other nine).
|
|
881
|
+
//
|
|
882
|
+
// apps/api/src/services/listing-promotion.ts has ZERO production
|
|
883
|
+
// importers anywhere in the repo (only its own test) — confirmed dead code
|
|
884
|
+
// (spec §7 Q5). Listed anyway: it is listing code by subject, D-48 places
|
|
885
|
+
// the instrument here, and a reachability grep cannot place an unreached
|
|
886
|
+
// file — this is a recorded pre-existing defect, not fixed by this card.
|
|
887
|
+
//
|
|
888
|
+
// apps/api/src/bin/deliverable-upload.ts imports only `prisma` (no moved
|
|
889
|
+
// service) but is a deliverables CLI a Free starter should not ship —
|
|
890
|
+
// listed by subject, not by the import-criterion the other six bins meet.
|
|
891
|
+
//
|
|
892
|
+
// apps/web: FlowStepperClient.tsx, ResultsClient.tsx and
|
|
893
|
+
// ListingApplicationClient.tsx are this module's OWN files (below), so
|
|
894
|
+
// their packages/ui imports (FlowStepper, FlowSidePanel,
|
|
895
|
+
// ListingApplicationFlow) are in-module edges, not Free ones, despite
|
|
896
|
+
// living under apps/web rather than apps-paid.
|
|
897
|
+
//
|
|
898
|
+
// packages/ui: eleven components (D8) plus the NEW slice registration
|
|
899
|
+
// entry point (D8a, mirroring sections/slices/scheduling.ts). Four of the
|
|
900
|
+
// eleven (ProjectListingShelves, ListingPrivacyNotice, ProjectListingCard,
|
|
901
|
+
// ResultsReveal) have no consumer anywhere in the repo today — grep-
|
|
902
|
+
// confirmed orphaned exports, not a module-boundary fact; D8 places them
|
|
903
|
+
// here by subject (project-listing / results-reveal UI) rather than by
|
|
904
|
+
// reachability, since reachability cannot place an unreached file either.
|
|
905
|
+
"signal-collectors": {
|
|
906
|
+
ownedFiles: [
|
|
907
|
+
"apps/api/src/routes/flows/index.ts",
|
|
908
|
+
// README.md documents the flows rate-limit rationale (#4730) — not a
|
|
909
|
+
// test/generated file, and its absence is exactly what left a stray
|
|
910
|
+
// apps/api/src/routes/flows/ directory in a keyless templates/ snapshot
|
|
911
|
+
// (empirically found, #5366) since the import-graph closure below only
|
|
912
|
+
// scans source extensions, never markdown.
|
|
913
|
+
"apps/api/src/routes/flows/README.md",
|
|
914
|
+
"apps/api/src/routes/deliverables/index.ts",
|
|
915
|
+
"apps/api/src/routes/slack/index.ts",
|
|
916
|
+
"apps/api/src/routes/slack/actions.ts",
|
|
917
|
+
"apps/api/src/routes/waitlist-signups/index.ts",
|
|
918
|
+
// apps/api/src/routes/project-listings/{start,patch-draft,submit,public,
|
|
919
|
+
// me,respond,resume-token}.ts and services/{project-listings,
|
|
920
|
+
// listing-config}.ts deliberately NOT listed — see the comment above
|
|
921
|
+
// this array (D-48/#5366 escalation: the mixed-barrel `index.ts` in
|
|
922
|
+
// that directory is reached from server.ts by a plain, literal
|
|
923
|
+
// `import()` for multi-tenant's tenant routes, and statically imports
|
|
924
|
+
// all nine of these files itself, so `paidModuleTemplateExclusions()`'s
|
|
925
|
+
// import-graph closure excluded `index.ts`, then `server.ts` itself,
|
|
926
|
+
// from every keyless scaffold (empirically found, #5366 resume pass,
|
|
927
|
+
// same failure mode as the deliverable-fulfillment.ts case below but a
|
|
928
|
+
// permanent structural one rather than a temporal gap pending another
|
|
929
|
+
// PR). This is also the ORIGINAL multi-tenant/#5088 card's own
|
|
930
|
+
// conclusion about these same nine files (lines 556-571 above) —
|
|
931
|
+
// D-48 moves their tier ownership to signal-collectors but not their
|
|
932
|
+
// must-ship-unconditionally constraint. Registration of every route
|
|
933
|
+
// that reaches them stays gated by `isModuleEnabled("signal-collectors")`
|
|
934
|
+
// in server.ts; only the source files themselves ship regardless.
|
|
935
|
+
"apps/api/src/services/flow-engine.ts",
|
|
936
|
+
"apps/api/src/services/flow-aggregate.ts",
|
|
937
|
+
"apps/api/src/services/waitlist-signups.ts",
|
|
938
|
+
"apps/api/src/services/waitlist-scoring.ts",
|
|
939
|
+
"apps/api/src/services/playbook-compile.ts",
|
|
940
|
+
"apps/api/src/services/playbook-render.ts",
|
|
941
|
+
"apps/api/src/services/listing-csv-sweep.ts",
|
|
942
|
+
"apps/api/src/services/waitlist-followup.ts",
|
|
943
|
+
"apps/api/src/services/listing-promotion.ts",
|
|
944
|
+
"apps/api/src/services/project-listing-decision.ts",
|
|
945
|
+
"apps/api/src/services/project-listing-flow.ts",
|
|
946
|
+
"apps/api/src/services/project-listing-issue.ts",
|
|
947
|
+
"apps/api/src/services/waitlist-experiments.ts",
|
|
948
|
+
// deliverable-fulfillment.ts deliberately NOT listed — see the comment
|
|
949
|
+
// above this array (spec §5.4 escalation boundary, temporary pending #5364).
|
|
950
|
+
"apps/api/src/bin/deliverable-resend.ts",
|
|
951
|
+
"apps/api/src/bin/deliverable-upload.ts",
|
|
952
|
+
"apps/api/src/bin/session-retention-sweep.ts",
|
|
953
|
+
"apps/api/src/bin/seed-presets.ts",
|
|
954
|
+
"apps/api/src/bin/listing-csv-sweep.ts",
|
|
955
|
+
"apps/api/src/bin/followup-sweep.ts",
|
|
956
|
+
"apps/api/src/bin/seed-waitlist-experiments.ts",
|
|
957
|
+
"apps/mcp/src/tools/get_flow.ts",
|
|
958
|
+
"apps/mcp/src/tools/submit_flow_step.ts",
|
|
959
|
+
// apps/web/app/[locale]/flows/** deliberately NOT listed here (unlike
|
|
960
|
+
// the docs-blog precedent's own `blog`/`docs` page.tsx entries above),
|
|
961
|
+
// even though every file under it is signal-collectors territory and
|
|
962
|
+
// still carries its own `oss: false` marker for the OSS-strip. Every
|
|
963
|
+
// one of these files is ALREADY excluded from a keyless scaffold by the
|
|
964
|
+
// pre-existing, independent `MODULE_SURFACES["signal-collectors"] =
|
|
965
|
+
// ["flows"]` → `excludedRoutePaths()` mechanism (#4154), which removes
|
|
966
|
+
// the WHOLE `apps/web/app/[locale]/flows` directory at copy time
|
|
967
|
+
// whenever the "flows" surface isn't selected — and `resolveSurfaces`
|
|
968
|
+
// refuses `--surfaces flows` without the module (#5365 FR5/D12), so
|
|
969
|
+
// there is no way to reach "flows carried, signal-collectors absent".
|
|
970
|
+
// Adding them to `ownedFiles` ALSO excludes them from `templates/`
|
|
971
|
+
// itself (build-templates.mjs), which broke `sync-routes`'s pre-#5366
|
|
972
|
+
// upgrade-path fixture (`flows/[slug]/results/[payload]`, #4891/#4986)
|
|
973
|
+
// and — more importantly — would silently strip a REAL consumer's
|
|
974
|
+
// `sync-routes` tool of the ability to ever restore these files after
|
|
975
|
+
// scaffolding, since that tool diffs against `templates/` alone and has
|
|
976
|
+
// no module/licence concept of its own (empirically found, #5366 resume
|
|
977
|
+
// pass). `projects/apply/ListingApplicationClient.tsx` below is
|
|
978
|
+
// different in kind: it sits under the UNRELATED "projects" content
|
|
979
|
+
// surface's URL segment, which `excludedRoutePaths` does not gate on
|
|
980
|
+
// signal-collectors at all, so it genuinely needs `ownedFiles` to be
|
|
981
|
+
// excluded from a keyless scaffold — see `sync-routes.ts`'s
|
|
982
|
+
// `NESTED_FOREIGN_MODULE_ENTRIES` for the companion fix that keeps its
|
|
983
|
+
// presence from falsely reading as "projects carried".
|
|
984
|
+
"apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx",
|
|
985
|
+
"packages/ui/src/sections/ListingApplicationFlow.tsx",
|
|
986
|
+
"packages/ui/src/sections/ProjectListingShelves.tsx",
|
|
987
|
+
"packages/ui/src/sections/FlowStepper.tsx",
|
|
988
|
+
"packages/ui/src/blocks/FlowSidePanel.tsx",
|
|
989
|
+
"packages/ui/src/blocks/ListingFullPreview.tsx",
|
|
990
|
+
"packages/ui/src/blocks/ListingQuestionReview.tsx",
|
|
991
|
+
"packages/ui/src/blocks/ListingTilePreview.tsx",
|
|
992
|
+
"packages/ui/src/blocks/ListingPrivacyNotice.tsx",
|
|
993
|
+
"packages/ui/src/blocks/ProjectListingCard.tsx",
|
|
994
|
+
"packages/ui/src/blocks/ResponseChart.tsx",
|
|
995
|
+
"packages/ui/src/blocks/ResultsReveal.tsx",
|
|
996
|
+
"packages/ui/src/sections/slices/signal-collectors.ts",
|
|
997
|
+
],
|
|
998
|
+
},
|
|
999
|
+
// ── commerce — per-site paid add-on (D-47, amending D-26) ─────────────────
|
|
1000
|
+
//
|
|
1001
|
+
// Checkout, the Stripe webhook receiver, flow checkouts and the recommender
|
|
1002
|
+
// (D-47's disposition sentence, verbatim). The base API keeps only
|
|
1003
|
+
// `isModuleEnabled("commerce")`; every registration loads through
|
|
1004
|
+
// `SLICE_PLUGINS` (server.ts). `apps.api.may_import_from` names
|
|
1005
|
+
// `packages/stripe-client` — the module's own package (D5) — mirroring the
|
|
1006
|
+
// `multi-tenant`/`scheduling` slices' identical entry, which already
|
|
1007
|
+
// document "checkout moved to the commerce module (D-47)" for the same
|
|
1008
|
+
// reason those two slices state theirs: `deriveArchitecture` merges `api`
|
|
1009
|
+
// blocks with `Object.assign`, so every slice declaring `api` must agree.
|
|
1010
|
+
//
|
|
1011
|
+
// Q1 (spec §7): a `multi-tenant` deployment using the Stripe billing portal
|
|
1012
|
+
// or org-scoped webhook routing (`routes/billing/portal.ts`,
|
|
1013
|
+
// `routes/webhooks/stripe.ts`'s org-billing branch) also depends on
|
|
1014
|
+
// `lib/stripe.ts` and the webhook receiver this module owns — declared
|
|
1015
|
+
// here, not engineered around.
|
|
1016
|
+
commerce: {
|
|
1017
|
+
apps: {
|
|
1018
|
+
api: {
|
|
1019
|
+
path: "apps/api",
|
|
1020
|
+
description: "Public HTTP API — routes + services (Fastify). Owns checkout, the Stripe webhook receiver, flow checkouts and the recommender (D-47) — the Stripe setup and stripe-client's sole apps/api reach.",
|
|
1021
|
+
may_import_from: [
|
|
1022
|
+
"database",
|
|
1023
|
+
"packages/analytics",
|
|
1024
|
+
"packages/email",
|
|
1025
|
+
"packages/license",
|
|
1026
|
+
"packages/schema",
|
|
1027
|
+
"packages/site-config",
|
|
1028
|
+
"packages/stripe-client",
|
|
1029
|
+
"packages/validation",
|
|
1030
|
+
],
|
|
1031
|
+
may_not_import_from: [
|
|
1032
|
+
"apps/mcp",
|
|
1033
|
+
"apps/web",
|
|
1034
|
+
"packages/theme",
|
|
1035
|
+
"packages/ui",
|
|
1036
|
+
],
|
|
1037
|
+
},
|
|
1038
|
+
},
|
|
1039
|
+
packages: {
|
|
1040
|
+
// Duplicated VERBATIM from the multi-tenant slice's own "stripe-client"
|
|
1041
|
+
// entry (§7 Q1 — a multi-tenant deployment using the Stripe billing
|
|
1042
|
+
// portal or org-billing webhook routing also installs commerce, so
|
|
1043
|
+
// both slices can be selected together) — the same order-independence
|
|
1044
|
+
// reason the multi-tenant/scheduling slices state for `validation`
|
|
1045
|
+
// below: `deriveArchitecture` merges `packages` blocks with
|
|
1046
|
+
// `Object.assign`, so two slices declaring this key differently would
|
|
1047
|
+
// make the derivation depend on module order.
|
|
1048
|
+
"stripe-client": {
|
|
1049
|
+
description: "Stripe SDK wrapper + webhook signature verification",
|
|
1050
|
+
may_import_from: ["packages/validation"],
|
|
1051
|
+
},
|
|
1052
|
+
// Duplicated VERBATIM from the marketing slice for the same
|
|
1053
|
+
// order-independence reason the multi-tenant/scheduling slices state.
|
|
1054
|
+
validation: {
|
|
1055
|
+
description: "Zod schemas + shared types",
|
|
1056
|
+
may_import_from: [],
|
|
1057
|
+
},
|
|
1058
|
+
},
|
|
1059
|
+
// webImports omitted (§4.4): no commerce component reaches apps/web
|
|
1060
|
+
// through a package apps/web does not already import — packages/ui is
|
|
1061
|
+
// base, and architecture.yaml already forbids apps/web -> stripe-client.
|
|
1062
|
+
forbiddenPatterns: [
|
|
1063
|
+
{
|
|
1064
|
+
pattern: STRIPE_LIVE_KEY_PATTERN,
|
|
1065
|
+
paths_excluded: ["apps/api/**"],
|
|
1066
|
+
reason: "The commerce credential belongs to apps/api, behind assertSandboxKey's sandbox-only allow-list. A live-mode-shaped key anywhere else is a leak, not a config choice",
|
|
1067
|
+
},
|
|
1068
|
+
],
|
|
1069
|
+
// ownedFiles (S1, #5088). SOURCE files only (#5182 D6) — no __tests__/,
|
|
1070
|
+
// no .stories.tsx, no .text.ts... except .text.ts siblings ARE source
|
|
1071
|
+
// (they ship as runtime copy modules, not test fixtures), so the ten
|
|
1072
|
+
// annotated below are listed. 42 in all: 9 apps/api + 26 packages/ui
|
|
1073
|
+
// (15 components + 10 .text.ts siblings + this slice's own
|
|
1074
|
+
// sections/slices/commerce.ts) + 7 apps/web.
|
|
1075
|
+
ownedFiles: [
|
|
1076
|
+
"apps/api/src/lib/stripe.ts",
|
|
1077
|
+
"apps/api/src/routes/checkout/index.ts",
|
|
1078
|
+
"apps/api/src/routes/checkout/sessions.ts",
|
|
1079
|
+
"apps/api/src/routes/webhooks/index.ts",
|
|
1080
|
+
"apps/api/src/routes/webhooks/stripe.ts",
|
|
1081
|
+
// README.md documents the Stripe webhook receiver — not a test/
|
|
1082
|
+
// generated file, and its absence is exactly what left a stray
|
|
1083
|
+
// apps/api/src/routes/webhooks/ directory in a keyless templates/
|
|
1084
|
+
// snapshot (empirically found, #5364 build pass): the import-graph
|
|
1085
|
+
// closure only scans source extensions, never markdown, so once
|
|
1086
|
+
// index.ts and stripe.ts are excluded this is the one file left
|
|
1087
|
+
// behind. Same defect class #5366 found and fixed the same way for
|
|
1088
|
+
// routes/flows/README.md — see that entry above.
|
|
1089
|
+
"apps/api/src/routes/webhooks/README.md",
|
|
1090
|
+
"apps/api/src/routes/flow-checkouts/index.ts",
|
|
1091
|
+
"apps/api/src/routes/flows/recommender.ts",
|
|
1092
|
+
"apps/api/src/services/recommender-capture.ts",
|
|
1093
|
+
"apps/api/src/services/flow-checkouts.ts",
|
|
1094
|
+
"packages/ui/src/blocks/CheckoutForm.tsx",
|
|
1095
|
+
"packages/ui/src/blocks/CheckoutForm.text.ts",
|
|
1096
|
+
"packages/ui/src/blocks/CheckoutPriceCard.tsx",
|
|
1097
|
+
"packages/ui/src/blocks/PaymentLinkCard.tsx",
|
|
1098
|
+
"packages/ui/src/blocks/PaymentLinkCard.text.ts",
|
|
1099
|
+
"packages/ui/src/blocks/PaymentLinkForm.tsx",
|
|
1100
|
+
"packages/ui/src/blocks/PaymentLinkForm.text.ts",
|
|
1101
|
+
"packages/ui/src/blocks/PaymentLinkList.tsx",
|
|
1102
|
+
"packages/ui/src/blocks/InvoiceForm.tsx",
|
|
1103
|
+
"packages/ui/src/blocks/InvoiceForm.text.ts",
|
|
1104
|
+
"packages/ui/src/blocks/InvoiceList.tsx",
|
|
1105
|
+
"packages/ui/src/blocks/InvoiceList.text.ts",
|
|
1106
|
+
"packages/ui/src/blocks/SubscriptionStatus.tsx",
|
|
1107
|
+
"packages/ui/src/blocks/SubscriptionStatus.text.ts",
|
|
1108
|
+
"packages/ui/src/blocks/StripeActionButton.tsx",
|
|
1109
|
+
"packages/ui/src/blocks/PaymentStatusBadge.tsx",
|
|
1110
|
+
"packages/ui/src/blocks/PaymentStatusBadge.text.ts",
|
|
1111
|
+
"packages/ui/src/blocks/PaymentResult.tsx",
|
|
1112
|
+
"packages/ui/src/blocks/PaymentResult.text.ts",
|
|
1113
|
+
"packages/ui/src/blocks/PortalEntry.tsx",
|
|
1114
|
+
"packages/ui/src/panels/InvoiceDetail.tsx",
|
|
1115
|
+
"packages/ui/src/panels/InvoiceDetail.text.ts",
|
|
1116
|
+
"packages/ui/src/sections/PaymentReceipt.tsx",
|
|
1117
|
+
"packages/ui/src/sections/PricingTable.tsx",
|
|
1118
|
+
"packages/ui/src/sections/PricingTable.text.ts",
|
|
1119
|
+
"packages/ui/src/sections/slices/commerce.ts",
|
|
1120
|
+
"apps/web/app/[locale]/flows/[slug]/checkout.actions.ts",
|
|
1121
|
+
"apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx",
|
|
1122
|
+
"apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx",
|
|
1123
|
+
"apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx",
|
|
1124
|
+
"apps/web/app/[locale]/dev/purchase/page.tsx",
|
|
1125
|
+
"apps/web/app/[locale]/dev/purchase/error.tsx",
|
|
1126
|
+
"apps/web/app/[locale]/dev/purchase/purchase.actions.ts",
|
|
1127
|
+
],
|
|
1128
|
+
// The scaffold-commerce authoring skill (D-47, #5449): moved by `git mv`
|
|
1129
|
+
// out of the Free starter's `templates/.claude/skills/` (never stripped —
|
|
1130
|
+
// it sits outside build-templates.mjs's SNAPSHOT_DIRS/overlay, so
|
|
1131
|
+
// ownedFiles/paidModuleTemplateExclusions cannot reach it) to
|
|
1132
|
+
// `module-templates/commerce/.claude/skills/scaffold-commerce/`, and
|
|
1133
|
+
// delivered into a keyed scaffold by `installMap` instead. Ten literal
|
|
1134
|
+
// entries, not one directory entry: `extractModuleBundle`'s
|
|
1135
|
+
// `validateInstallMap` requires every source to be a FILE the bundle
|
|
1136
|
+
// produced (`extract-module-bundle.ts`'s `!sourceStat.isFile()` check) —
|
|
1137
|
+
// a directory source is rejected outright.
|
|
1138
|
+
installMap: {
|
|
1139
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/SKILL.md": ".claude/skills/scaffold-commerce/SKILL.md",
|
|
1140
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/references/lookup-keys-template.md": ".claude/skills/scaffold-commerce/references/lookup-keys-template.md",
|
|
1141
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md": ".claude/skills/scaffold-commerce/references/marketplace-brand-pages.md",
|
|
1142
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts": ".claude/skills/scaffold-commerce/templates/checkout-route.template.ts",
|
|
1143
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts": ".claude/skills/scaffold-commerce/templates/invoice-route.template.ts",
|
|
1144
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl": ".claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl",
|
|
1145
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts": ".claude/skills/scaffold-commerce/templates/payment-link-route.template.ts",
|
|
1146
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/portal-route.template.ts": ".claude/skills/scaffold-commerce/templates/portal-route.template.ts",
|
|
1147
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts": ".claude/skills/scaffold-commerce/templates/stripe-config.template.ts",
|
|
1148
|
+
"packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts": ".claude/skills/scaffold-commerce/templates/subscription-route.template.ts",
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
646
1151
|
};
|
|
647
1152
|
/**
|
|
648
1153
|
* Normalize a requested module list to the unique set, ordered by the canonical
|
|
@@ -661,3 +1166,141 @@ export function normalizeModules(modules) {
|
|
|
661
1166
|
}
|
|
662
1167
|
return RECOGNIZED_MODULES.filter((m) => requested.has(m));
|
|
663
1168
|
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Template-relative paths that must never be written into `templates/` for a
|
|
1171
|
+
* keyless starter: every PAID module's `ownedFiles` entry that resolves under
|
|
1172
|
+
* a `build-templates` snapshot root, plus every file under those roots that
|
|
1173
|
+
* imports one (transitively), which would otherwise ship dangling.
|
|
1174
|
+
*
|
|
1175
|
+
* This is the SINGLE starter-removal input for all four R1 extractions
|
|
1176
|
+
* (#5364 commerce, #5365 docs + blog, #5366 signal-collectors, #5367 Pro Plus
|
|
1177
|
+
* + scheduling). A module joins by gaining `ownedFiles` entries — never by
|
|
1178
|
+
* adding a path to a list here.
|
|
1179
|
+
*
|
|
1180
|
+
* The input is `ownedFiles`, NOT the `oss: false` marker set. The two answer
|
|
1181
|
+
* different questions: the strip's authority is their union (see the
|
|
1182
|
+
* `ownedFiles` doc above), while the STARTER's authority is `ownedFiles`
|
|
1183
|
+
* alone. The marker set includes the content-write trio, which D-44 keeps in
|
|
1184
|
+
* public code licence-checked at runtime — deriving the starter exclusion from
|
|
1185
|
+
* markers would remove a surface D-44 says must ship.
|
|
1186
|
+
*
|
|
1187
|
+
* Entries outside the snapshot roots — every `packages/**` path, and the paid
|
|
1188
|
+
* app roots — are not snapshotted at all and are filtered here rather than
|
|
1189
|
+
* special-cased at the call site. Today every glob entry is also out-of-root,
|
|
1190
|
+
* so globs never reach the literal-path matcher below.
|
|
1191
|
+
*
|
|
1192
|
+
* @param snapshotRoots repo-relative roots `build-templates.mjs` snapshots.
|
|
1193
|
+
* @param repoRoot absolute path to the monorepo root, for the import scan.
|
|
1194
|
+
* Passed in rather than derived from `import.meta.url`, which ts-jest cannot
|
|
1195
|
+
* load — the same reason `sync-routes.ts` documents for staying pure.
|
|
1196
|
+
*/
|
|
1197
|
+
export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
|
|
1198
|
+
const roots = new Set(snapshotRoots);
|
|
1199
|
+
const excluded = new Set();
|
|
1200
|
+
for (const moduleName of PAID_MODULES) {
|
|
1201
|
+
for (const owned of MODULE_SLICES[moduleName]?.ownedFiles ?? []) {
|
|
1202
|
+
const rel = owned.split(sep).join("/");
|
|
1203
|
+
if (!roots.has(rel.split("/")[0] ?? ""))
|
|
1204
|
+
continue;
|
|
1205
|
+
if (rel.includes("*"))
|
|
1206
|
+
continue;
|
|
1207
|
+
excluded.add(rel);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
// Companion closure by import graph, to a fixed point — never a filename
|
|
1211
|
+
// stem rule (#5182 D7), which misses eleven files and over-marks three.
|
|
1212
|
+
const candidates = listFilesUnder(repoRoot, [...roots]);
|
|
1213
|
+
for (;;) {
|
|
1214
|
+
let grew = false;
|
|
1215
|
+
for (const file of candidates) {
|
|
1216
|
+
if (excluded.has(file))
|
|
1217
|
+
continue;
|
|
1218
|
+
const imports = relativeImportsOf(join(repoRoot, file));
|
|
1219
|
+
for (const spec of imports) {
|
|
1220
|
+
const target = resolveRelativeImport(file, spec, candidates);
|
|
1221
|
+
if (target !== undefined && excluded.has(target)) {
|
|
1222
|
+
excluded.add(file);
|
|
1223
|
+
grew = true;
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (!grew)
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
return [...excluded].sort();
|
|
1232
|
+
}
|
|
1233
|
+
const SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1234
|
+
/** Every source file under the given repo-relative roots, as "/"-joined paths. */
|
|
1235
|
+
function listFilesUnder(repoRoot, roots) {
|
|
1236
|
+
const out = new Set();
|
|
1237
|
+
const skip = new Set(["node_modules", ".next", "dist", ".turbo", ".source"]);
|
|
1238
|
+
const stack = roots.map((r) => r);
|
|
1239
|
+
while (stack.length > 0) {
|
|
1240
|
+
const rel = stack.pop();
|
|
1241
|
+
const abs = join(repoRoot, rel);
|
|
1242
|
+
let entries;
|
|
1243
|
+
try {
|
|
1244
|
+
entries = readdirSync(abs, { withFileTypes: true });
|
|
1245
|
+
}
|
|
1246
|
+
catch {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
for (const entry of entries) {
|
|
1250
|
+
if (skip.has(entry.name))
|
|
1251
|
+
continue;
|
|
1252
|
+
const childRel = `${rel}/${entry.name}`;
|
|
1253
|
+
if (entry.isDirectory())
|
|
1254
|
+
stack.push(childRel);
|
|
1255
|
+
else if (SOURCE_EXTS.some((e) => entry.name.endsWith(e)))
|
|
1256
|
+
out.add(childRel);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return out;
|
|
1260
|
+
}
|
|
1261
|
+
/** The relative import specifiers a source file names (static imports + re-exports). */
|
|
1262
|
+
function relativeImportsOf(absPath) {
|
|
1263
|
+
let src;
|
|
1264
|
+
try {
|
|
1265
|
+
src = readFileSync(absPath, "utf8");
|
|
1266
|
+
}
|
|
1267
|
+
catch {
|
|
1268
|
+
return [];
|
|
1269
|
+
}
|
|
1270
|
+
const out = [];
|
|
1271
|
+
for (const m of src.matchAll(/(?:^|\n)\s*(?:import|export)[\s\S]{0,400}?from\s*["'](\.[^"']*)["']/g))
|
|
1272
|
+
out.push(m[1]);
|
|
1273
|
+
for (const m of src.matchAll(/\bimport\s*\(\s*["'](\.[^"']*)["']\s*\)/g))
|
|
1274
|
+
out.push(m[1]);
|
|
1275
|
+
return out;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Resolve a relative specifier against the candidate set. TypeScript ESM writes
|
|
1279
|
+
* `./x.js` for `./x.ts`, so the `.js` → `.ts`/`.tsx` rewrite is the common case.
|
|
1280
|
+
*/
|
|
1281
|
+
function resolveRelativeImport(fromFile, spec, candidates) {
|
|
1282
|
+
const base = posixJoin(dirname(fromFile.split("/").join(sep)), spec);
|
|
1283
|
+
const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
|
|
1284
|
+
for (const cand of [
|
|
1285
|
+
base,
|
|
1286
|
+
...SOURCE_EXTS.map((e) => stem + e),
|
|
1287
|
+
...SOURCE_EXTS.map((e) => `${stem}/index${e}`),
|
|
1288
|
+
])
|
|
1289
|
+
if (candidates.has(cand))
|
|
1290
|
+
return cand;
|
|
1291
|
+
return undefined;
|
|
1292
|
+
}
|
|
1293
|
+
/** Join and normalize a "/"-separated path, resolving `.` and `..` segments. */
|
|
1294
|
+
function posixJoin(dir, spec) {
|
|
1295
|
+
const parts = `${dir.split(sep).join("/")}/${spec}`.split("/");
|
|
1296
|
+
const out = [];
|
|
1297
|
+
for (const part of parts) {
|
|
1298
|
+
if (part === "" || part === ".")
|
|
1299
|
+
continue;
|
|
1300
|
+
if (part === "..")
|
|
1301
|
+
out.pop();
|
|
1302
|
+
else
|
|
1303
|
+
out.push(part);
|
|
1304
|
+
}
|
|
1305
|
+
return out.join("/");
|
|
1306
|
+
}
|