create-ailk 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +6 -5
- package/NOTICE +2 -0
- package/dist/add-module.js +11 -3
- package/dist/category-profile.d.ts +10 -0
- package/dist/category-profile.js +21 -0
- package/dist/derive-architecture.d.ts +5 -1
- package/dist/derive-architecture.js +8 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/license-key-files.d.ts +6 -3
- package/dist/lib/license-key-files.js +8 -2
- package/dist/lib/scaffold-render-sweep.d.ts +32 -0
- package/dist/lib/scaffold-render-sweep.js +76 -3
- package/dist/license-set.d.ts +1 -1
- package/dist/license-set.js +13 -8
- package/dist/module-architecture.js +128 -3
- package/dist/programmatic.js +11 -1
- package/dist/site-roster.js +21 -4
- package/dist/spoke-checkout.d.ts +11 -3
- package/dist/spoke-checkout.js +62 -12
- package/dist/starter-grid.d.ts +1 -1
- package/dist/surfaces.d.ts +14 -0
- package/dist/surfaces.js +170 -1
- package/dist/template-manifest.d.ts +4 -3
- package/dist/topology.d.ts +7 -2
- package/dist/topology.js +88 -3
- package/package.json +5 -4
- package/templates/.env.example +3 -3
- package/templates/.github/workflows/ci.yml +5 -0
- package/templates/apps/api/.env.example +17 -1
- package/templates/apps/api/CLAUDE.md +31 -7
- package/templates/apps/api/LICENSE +6 -5
- package/templates/apps/api/NOTICE +2 -0
- package/templates/apps/api/jest.config.cjs +4 -29
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +19 -0
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +132 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +17 -0
- package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +237 -0
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +334 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +38 -0
- package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +217 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +7 -0
- package/templates/apps/api/src/lib/consent-store.ts +335 -0
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +5 -4
- package/templates/apps/api/src/lib/stripe.ts +176 -3
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +272 -39
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +51 -0
- package/templates/apps/api/src/middleware/auth.ts +214 -46
- package/templates/apps/api/src/middleware/rate-limit.ts +5 -0
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +167 -0
- package/templates/apps/api/src/routes/checkout/session.ts +3 -3
- package/templates/apps/api/src/routes/checkout/sessions.ts +6 -4
- package/templates/apps/api/src/routes/consent/cookie.ts +90 -0
- package/templates/apps/api/src/routes/consent/index.ts +18 -0
- package/templates/apps/api/src/routes/consent/unsubscribe.ts +127 -0
- package/templates/apps/api/src/routes/leads/index.ts +19 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +5 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +17 -4
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +2 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +159 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +158 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +2 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +17 -0
- package/templates/apps/api/src/server.ts +218 -16
- package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +503 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +18 -3
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +173 -4
- package/templates/apps/api/src/services/__tests__/person.test.ts +392 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +87 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +57 -17
- package/templates/apps/api/src/services/consent.ts +488 -3
- package/templates/apps/api/src/services/lead-routing.ts +135 -14
- package/templates/apps/api/src/services/person.ts +501 -1
- package/templates/apps/api/src/services/project-listings.ts +7 -2
- package/templates/apps/api/src/services/request-confirm.ts +52 -0
- package/templates/apps/api/src/services/revalidation.ts +11 -1
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/.env.example +10 -0
- package/templates/apps/mcp/CLAUDE.md +9 -4
- package/templates/apps/mcp/LICENSE +6 -5
- package/templates/apps/mcp/NOTICE +2 -0
- package/templates/apps/mcp/__tests__/abuse/guard.test.ts +198 -0
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +15 -3
- package/templates/apps/mcp/__tests__/boot.test.ts +78 -1
- package/templates/apps/mcp/__tests__/http-transport.test.ts +291 -2
- package/templates/apps/mcp/__tests__/request_callback.test.ts +69 -7
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +69 -8
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +69 -7
- package/templates/apps/mcp/jest.config.cjs +3 -44
- package/templates/apps/mcp/src/abuse/index.ts +117 -21
- package/templates/apps/mcp/src/boot.ts +5 -23
- package/templates/apps/mcp/src/http-transport.ts +93 -73
- package/templates/apps/mcp/src/index.ts +2 -0
- package/templates/apps/mcp/src/request-context.ts +52 -0
- package/templates/apps/mcp/src/tools/capture_lead.ts +9 -1
- package/templates/apps/mcp/src/tools/request_callback.ts +9 -1
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +9 -1
- package/templates/apps/web/LICENSE +6 -5
- package/templates/apps/web/NOTICE +2 -0
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/account-frame-sign-out.test.tsx +175 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +302 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +724 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +346 -41
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +11 -13
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +146 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +232 -29
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +305 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-popups.test.tsx +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +140 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +163 -72
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +451 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +409 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +65 -380
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/support.test.tsx +16 -11
- package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +1 -1
- package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +188 -54
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +24 -45
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +253 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +145 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +100 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +179 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +514 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +288 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +123 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +174 -0
- package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +172 -0
- package/templates/apps/web/app/[locale]/(authed)/account/billing-source.ts +147 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/request-quote.tsx +229 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/schedule-service.tsx +276 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/send-message.tsx +128 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/billing-anchor.tsx +27 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/contact-actions.ts +81 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/deletion-request.tsx +132 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +193 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/profile-form.tsx +104 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/profile-source.ts +73 -0
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +18 -8
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +4 -6
- package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +30 -4
- package/templates/apps/web/app/[locale]/(authed)/account/new-actions.ts +219 -0
- package/templates/apps/web/app/[locale]/(authed)/account/new-menu.tsx +340 -0
- package/templates/apps/web/app/[locale]/(authed)/account/new-popups.tsx +107 -0
- package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +57 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +85 -26
- package/templates/apps/web/app/[locale]/(authed)/account/profile-actions.ts +116 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +132 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/security-panel.tsx +102 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/security-source.ts +62 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +13 -354
- package/templates/apps/web/app/[locale]/(authed)/account/settings-frame.tsx +116 -0
- package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +37 -0
- package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +9 -10
- package/templates/apps/web/app/[locale]/layout.tsx +23 -4
- package/templates/apps/web/app/api/auth/hub/__tests__/continuation.test.ts +143 -0
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +62 -10
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +22 -7
- package/templates/apps/web/app/api/auth/hub/continuation.ts +73 -0
- package/templates/apps/web/app/api/auth/hub/continue/[ref]/__tests__/route.test.ts +188 -0
- package/templates/apps/web/app/api/auth/hub/continue/[ref]/route.ts +53 -0
- package/templates/apps/web/app/robots.txt/route.ts +10 -4
- package/templates/apps/web/auth.ts +25 -2
- package/templates/apps/web/lib/__tests__/account-config.test.ts +258 -46
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +84 -0
- package/templates/apps/web/lib/account-config.ts +188 -52
- package/templates/apps/web/lib/auth-email.ts +45 -10
- package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +143 -0
- package/templates/apps/web/lib/hub-session/index.ts +1 -0
- package/templates/apps/web/lib/hub-session/reseal.ts +90 -0
- package/templates/apps/web/lib/hub-session/seal.ts +13 -9
- package/templates/apps/web/lib/topology.generated.ts +33 -0
- package/templates/apps/web/messages/en.json +194 -33
- package/templates/database/CHANGELOG.md +37 -0
- package/templates/database/LICENSE +6 -5
- package/templates/database/NOTICE +2 -0
- package/templates/database/README.md +1 -1
- package/templates/database/__tests__/consent-ledger.db.test.ts +345 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +6 -2
- package/templates/database/__tests__/fixtures/person-contact-backfill.json +182 -0
- package/templates/database/__tests__/member-area.db.test.ts +15 -3
- package/templates/database/__tests__/member-contract-migration.test.ts +85 -0
- package/templates/database/__tests__/member-contract.db.test.ts +413 -0
- package/templates/database/__tests__/person-contact.db.test.ts +563 -0
- package/templates/database/__tests__/person.db.test.ts +3 -1
- package/templates/database/__tests__/seed.test.ts +54 -2
- package/templates/database/inbox/schema.prisma +166 -25
- package/templates/database/migrations/20261012120000_member_contract/migration.sql +192 -0
- package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +20 -0
- package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +122 -0
- package/templates/database/migrations/20261014120000_person_contact/migration.sql +231 -0
- package/templates/database/package.json +3 -2
- package/templates/database/seed.ts +34 -2
- package/versions.json +36 -36
- package/templates/apps/api/src/__tests__/content-write-production-boot.test.ts +0 -104
- package/templates/apps/api/src/__tests__/cors.test.ts +0 -276
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +0 -279
- package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +0 -122
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +0 -167
- package/templates/apps/api/src/__tests__/root-modules.test.ts +0 -175
- package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +0 -117
- package/templates/apps/api/src/__tests__/server.test.ts +0 -591
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +0 -175
- package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +0 -293
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +0 -1620
- package/templates/apps/api/src/routes/project-listings/__tests__/directory.test.ts +0 -528
- package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +0 -198
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +0 -652
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -408
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +0 -476
- package/templates/apps/mcp/__tests__/audit.test.ts +0 -346
- package/templates/apps/mcp/__tests__/parity.test.ts +0 -325
- package/templates/apps/mcp/__tests__/workspace-surface.test.ts +0 -364
- package/templates/apps/web/app/[locale]/(authed)/account/settings/color-mode.tsx +0 -68
- package/templates/apps/web/app/[locale]/(authed)/account/settings/deletion-request.tsx +0 -55
- package/templates/apps/web/app/[locale]/(authed)/account/settings/name-form.tsx +0 -118
- package/templates/database/__tests__/build.test.ts +0 -131
|
@@ -77,8 +77,8 @@ const PUBLIC_SCHEDULING_VAR_PATTERN = [
|
|
|
77
77
|
* live-key-shaped token in this file's source would itself trip the repo's
|
|
78
78
|
* own live-key hard block (`rules/environment-boundaries.md`). `apps/api` is
|
|
79
79
|
* excluded because `STRIPE_SECRET_KEY` legitimately lives there, gated by
|
|
80
|
-
*
|
|
81
|
-
* `
|
|
80
|
+
* lib/stripe.ts's admitted-key guard at request time and by
|
|
81
|
+
* `admitCommerceStripeKey` at boot (server.ts) — this pattern catches a
|
|
82
82
|
* live-shaped key leaking anywhere ELSE (a fixture, a client component, a
|
|
83
83
|
* `NEXT_PUBLIC_` var).
|
|
84
84
|
*/
|
|
@@ -157,15 +157,43 @@ export const ARCHITECTURE_BASE = {
|
|
|
157
157
|
* file behind it — the three operator route files, the two member route
|
|
158
158
|
* files, the member resolution (`services/member-area.ts`) and the member
|
|
159
159
|
* data boundary (`lib/member-store.ts`).
|
|
160
|
+
*
|
|
161
|
+
* #8169 D9 adds the five Phase 1 member reads registered under that same
|
|
162
|
+
* disjunction. Its two other reads — `routes/member/meeting-types.ts` and
|
|
163
|
+
* `availability.ts` — reach `services/scheduling/`, so they are the
|
|
164
|
+
* `scheduling` slice's, not listed here.
|
|
165
|
+
*
|
|
166
|
+
* #8170 D11 adds the four member writes registered under that same
|
|
167
|
+
* disjunction (the message POST rides `messages.ts`). The fifth —
|
|
168
|
+
* `routes/member/bookings.ts` — reaches `services/scheduling/`, so it is the
|
|
169
|
+
* `scheduling` slice's.
|
|
160
170
|
*/
|
|
161
171
|
const MEMBER_AREA_FILES = [
|
|
162
172
|
"apps/api/src/routes/workspaces/pieces.ts",
|
|
163
173
|
"apps/api/src/routes/workspaces/plan.ts",
|
|
164
174
|
"apps/api/src/routes/workspaces/messages-out.ts",
|
|
175
|
+
// #8171 D8 — the workspace Requests read; imports only pieces.ts,
|
|
176
|
+
// lib/tenant-db.ts and @working-theory/validation.
|
|
177
|
+
"apps/api/src/routes/workspaces/requests.ts",
|
|
165
178
|
"apps/api/src/routes/member/pieces.ts",
|
|
166
179
|
"apps/api/src/routes/member/messages.ts",
|
|
180
|
+
"apps/api/src/routes/member/purchases.ts",
|
|
181
|
+
"apps/api/src/routes/member/appointments.ts",
|
|
182
|
+
"apps/api/src/routes/member/artifacts.ts",
|
|
183
|
+
"apps/api/src/routes/member/request-form.ts",
|
|
184
|
+
"apps/api/src/routes/member/billing.ts",
|
|
185
|
+
"apps/api/src/routes/member/requests.ts",
|
|
186
|
+
"apps/api/src/routes/member/artifact-review.ts",
|
|
187
|
+
"apps/api/src/routes/member/profile.ts",
|
|
188
|
+
"apps/api/src/routes/member/deletion-request.ts",
|
|
189
|
+
// #8438 D6 — the member's own consents; the same gate as the writes above.
|
|
190
|
+
"apps/api/src/routes/member/consents.ts",
|
|
167
191
|
"apps/api/src/services/member-area.ts",
|
|
168
192
|
"apps/api/src/lib/member-store.ts",
|
|
193
|
+
// #8226 D12 — the member-request match CLI; both import member-store.ts,
|
|
194
|
+
// which a build without the member slice does not carry.
|
|
195
|
+
"apps/api/src/lib/member-request-match.ts",
|
|
196
|
+
"apps/api/src/bin/member-request-match.ts",
|
|
169
197
|
];
|
|
170
198
|
/**
|
|
171
199
|
* The workspace asset store (#5854), CO-OWNED by the `workspace` and
|
|
@@ -759,6 +787,10 @@ export const MODULE_SLICES = {
|
|
|
759
787
|
"apps/api/src/routes/workspaces/index.ts",
|
|
760
788
|
"apps/api/src/routes/workspaces/sessions.ts",
|
|
761
789
|
"apps/api/src/routes/workspaces/config.ts",
|
|
790
|
+
// #8438 D8 — the Admin owner's consents to Working Theory: registered
|
|
791
|
+
// under `isModuleEnabled("multi-tenant")` alone, the module that
|
|
792
|
+
// contributes the Admin app.
|
|
793
|
+
"apps/api/src/routes/consent/platform.ts",
|
|
762
794
|
// Co-owned with `workspace` (#5853): the content-write scope is gated on
|
|
763
795
|
// content-write AND (workspace || multi-tenant) and this file imports
|
|
764
796
|
// lib/tenant-db.ts, so each tenancy slice must deliver it.
|
|
@@ -864,6 +896,18 @@ export const MODULE_SLICES = {
|
|
|
864
896
|
"apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
|
|
865
897
|
"apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
|
|
866
898
|
"apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
|
|
899
|
+
// #7389 — the three api suites that enable `multi-tenant` ALONE and so
|
|
900
|
+
// reach files this slice delivers (`services/tenant-context.ts` and the
|
|
901
|
+
// `routes/workspaces/` index). Each is listed individually, never as a
|
|
902
|
+
// glob (the `scheduling` slice's § D12 property 1: a glob entry is a
|
|
903
|
+
// silent no-op), so it ships to a keyed scaffold with the slice and is
|
|
904
|
+
// stripped from a keyless one with it — instead of a keyless scaffold
|
|
905
|
+
// carrying a test that raises `createNoMappedModuleFoundError`. A suite
|
|
906
|
+
// that enables SEVERAL paid modules cannot be owned by any one of
|
|
907
|
+
// them; those are `EXCLUDE_TEMPLATE_DIRS` entries below.
|
|
908
|
+
"apps/api/src/services/__tests__/lead-promotion.test.ts",
|
|
909
|
+
"apps/api/src/services/__tests__/leads-inbox.test.ts",
|
|
910
|
+
"apps/api/src/__tests__/content-write-production-boot.test.ts",
|
|
867
911
|
"apps-paid/admin/**",
|
|
868
912
|
],
|
|
869
913
|
// The whole-app glob above lands its files at their REPO-relative path
|
|
@@ -1232,6 +1276,13 @@ export const MODULE_SLICES = {
|
|
|
1232
1276
|
// records: "each tenancy slice must deliver it").
|
|
1233
1277
|
"apps/api/src/lib/tenant-db.ts",
|
|
1234
1278
|
"apps/api/src/services/tenant-context.ts",
|
|
1279
|
+
// #7389 — `routes/workspaces/__tests__/meetings.test.ts` and
|
|
1280
|
+
// `meetings-connection.test.ts` (listed below) register the real
|
|
1281
|
+
// `tenantPreHandlerPlugin` in front of `meetings.ts`, so a scheduling
|
|
1282
|
+
// scaffold without `multi-tenant` could not load them. It imports only
|
|
1283
|
+
// `services/tenant-context.ts` (owned above) and the base
|
|
1284
|
+
// `default-tenant.ts`, so co-owning it keeps the closure tight.
|
|
1285
|
+
"apps/api/src/middleware/tenant.ts",
|
|
1235
1286
|
// #7533 — a scheduling connection per workspace. `connections.ts` (the
|
|
1236
1287
|
// cipher + the one SchedulingConnection data-access boundary) and
|
|
1237
1288
|
// `resolve.ts` (the workspace-aware provider resolution the meetings,
|
|
@@ -1248,6 +1299,23 @@ export const MODULE_SLICES = {
|
|
|
1248
1299
|
"apps/api/src/routes/schedule/__tests__/public-connection.test.ts",
|
|
1249
1300
|
"apps/api/src/routes/webhooks/__tests__/calcom-connection.test.ts",
|
|
1250
1301
|
"apps/api/src/config/__tests__/scheduling-credential-key.test.ts",
|
|
1302
|
+
// #8169 D9 — the two member reads that reach `services/scheduling/`,
|
|
1303
|
+
// registered under `scheduling && (workspace || multi-tenant)`, plus
|
|
1304
|
+
// the three member files they import, which this slice then CO-OWNS
|
|
1305
|
+
// with the two tenancy slices (`MEMBER_AREA_FILES`) — exactly as it
|
|
1306
|
+
// co-owns `lib/tenant-db.ts` for `routes/workspaces/meetings.ts` above.
|
|
1307
|
+
"apps/api/src/routes/member/meeting-types.ts",
|
|
1308
|
+
"apps/api/src/routes/member/availability.ts",
|
|
1309
|
+
// #8170 D11 — the member booking, the same shape.
|
|
1310
|
+
"apps/api/src/routes/member/bookings.ts",
|
|
1311
|
+
"apps/api/src/routes/member/pieces.ts",
|
|
1312
|
+
"apps/api/src/services/member-area.ts",
|
|
1313
|
+
"apps/api/src/lib/member-store.ts",
|
|
1314
|
+
// #7389 — the two api suites that enable `scheduling` alone, so they
|
|
1315
|
+
// strip with `routes/schedule/index.ts`, which they load by a computed
|
|
1316
|
+
// specifier the import-graph closure cannot follow.
|
|
1317
|
+
"apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts",
|
|
1318
|
+
"apps/api/src/__tests__/openapi-keyless-hub.test.ts",
|
|
1251
1319
|
],
|
|
1252
1320
|
// pageContributions (spec 5478; parent § D6 shape 1) — the booker, the
|
|
1253
1321
|
// per-booking-type page and the confirmation page, whose route files
|
|
@@ -1463,6 +1531,10 @@ export const MODULE_SLICES = {
|
|
|
1463
1531
|
"apps/api/src/bin/listing-csv-sweep.ts",
|
|
1464
1532
|
"apps/api/src/bin/followup-sweep.ts",
|
|
1465
1533
|
"apps/api/src/bin/seed-waitlist-experiments.ts",
|
|
1534
|
+
// #7389 — the listing-directory suite enables `signal-collectors` alone
|
|
1535
|
+
// and loads `routes/waitlist-signups/index.ts` through the server it
|
|
1536
|
+
// builds, so it strips with that route. Individually, never as a glob.
|
|
1537
|
+
"apps/api/src/routes/project-listings/__tests__/directory.test.ts",
|
|
1466
1538
|
"apps/mcp/src/tools/get_flow.ts",
|
|
1467
1539
|
"apps/mcp/src/tools/submit_flow_step.ts",
|
|
1468
1540
|
// #3591 PR-E — the signed-in MCP tools that twin the tenant-scoped
|
|
@@ -1777,7 +1849,7 @@ export const MODULE_SLICES = {
|
|
|
1777
1849
|
{
|
|
1778
1850
|
pattern: STRIPE_LIVE_KEY_PATTERN,
|
|
1779
1851
|
paths_excluded: ["apps/api/**"],
|
|
1780
|
-
reason: "The commerce credential belongs to apps/api, behind
|
|
1852
|
+
reason: "The commerce credential belongs to apps/api, behind lib/stripe.ts's admitted-key guard. A live-mode-shaped key anywhere else is a leak, not a config choice",
|
|
1781
1853
|
},
|
|
1782
1854
|
],
|
|
1783
1855
|
// ownedFiles (S1, #5088). SOURCE files only (#5182 D6) — no __tests__/,
|
|
@@ -2013,6 +2085,44 @@ export function coOwnedModuleDestinations() {
|
|
|
2013
2085
|
// docs/specs/7315-a-gated-pro-tool-re-spec.md D4) — same reason as the
|
|
2014
2086
|
// three suites above: it imports `SITE_ARGS` from the monorepo's root
|
|
2015
2087
|
// `tests/parity/` tree, which no scaffold carries.
|
|
2088
|
+
// - apps/mcp/__tests__/write-tool-guard.test.ts (#8071, spec
|
|
2089
|
+
// docs/specs/8071-mcp-write-guard-spec.md D4) — it reads every anonymous
|
|
2090
|
+
// catalog tool's handler file, and a scaffold carries only the handler
|
|
2091
|
+
// files of the modules it selected while the catalog lists every tool.
|
|
2092
|
+
// `WRITE_TOOLS` is edited in the monorepo, which is where it must run.
|
|
2093
|
+
// - the suites a keyless scaffold's own `pnpm test` cannot run (#7389) —
|
|
2094
|
+
// file entries, matched exactly. Two classes, neither of which a slice can
|
|
2095
|
+
// own (a slice's `ownedFiles` carries a suite that needs ONE paid module;
|
|
2096
|
+
// the six api suites that do are listed under `multi-tenant`, `scheduling`
|
|
2097
|
+
// and `signal-collectors` above, and a keyed scaffold runs them):
|
|
2098
|
+
// * monorepo fixtures — each reads a path only this repo has:
|
|
2099
|
+
// `services/__tests__/aeo-score.test.ts` (packages/aeo's result.json),
|
|
2100
|
+
// `routes/leads/__tests__/index.test.ts` (packages/validation's
|
|
2101
|
+
// newsletter-lead fixture), `routes/workspaces/__tests__/
|
|
2102
|
+
// inbox-wire-schemas.test.ts` (the root `tests/parity/` core),
|
|
2103
|
+
// mcp's `workspace-surface.test.ts` (the same core) and mcp's
|
|
2104
|
+
// `audit.test.ts` (the T1 fixture site's `project.yaml`).
|
|
2105
|
+
// * whole-composition suites — each builds the real server or reads
|
|
2106
|
+
// the whole tree under SEVERAL module sets, or asserts the
|
|
2107
|
+
// monorepo's own identity, so no single slice is its owner and a
|
|
2108
|
+
// scaffold that selected only some of the modules would fail it:
|
|
2109
|
+
// `server.test.ts` (asserts the monorepo's "AI Launch Kit API" name,
|
|
2110
|
+
// and a `signal-collectors` case), `cors.test.ts` (multi-tenant +
|
|
2111
|
+
// scheduling), `root-modules.test.ts`, `module-exclusion.test.ts`,
|
|
2112
|
+
// `openapi/__tests__/surface-drift.test.ts`,
|
|
2113
|
+
// `review-surface-registration.test.ts` and
|
|
2114
|
+
// `workspace-module-registration.test.ts` (each a matrix over module
|
|
2115
|
+
// sets), and mcp's `parity.test.ts` (counts the Server Actions and
|
|
2116
|
+
// tool handlers of the FULL tree, which a stripped scaffold lacks).
|
|
2117
|
+
// The exclusion is per file, not a `testPathIgnorePatterns` entry in the
|
|
2118
|
+
// scaffold's jest config: that would hide these suites from a keyed
|
|
2119
|
+
// scaffold too, and from the suites a slice owns.
|
|
2120
|
+
// - database/__tests__/build.test.ts (#7959) — the monorepo build probe
|
|
2121
|
+
// (#7565). Its `beforeAll` symlinks `REPO_ROOT/packages` and copies
|
|
2122
|
+
// `REPO_ROOT/tsconfig.base.json`, neither of which a scaffold has, so it
|
|
2123
|
+
// red the scaffold's database jest run. Matched exactly, not by basename
|
|
2124
|
+
// in `EXCLUDE_FILES`, which would drop a `build.test.ts` anywhere in the
|
|
2125
|
+
// snapshot; every other database suite still ships.
|
|
2016
2126
|
export const EXCLUDE_TEMPLATE_DIRS = [
|
|
2017
2127
|
"apps/web/__tests__",
|
|
2018
2128
|
"apps/web/app/__tests__",
|
|
@@ -2023,6 +2133,21 @@ export const EXCLUDE_TEMPLATE_DIRS = [
|
|
|
2023
2133
|
"apps/api/src/__tests__/api-parity-paid.test.ts",
|
|
2024
2134
|
"apps/mcp/__tests__/api-parity.test.ts",
|
|
2025
2135
|
"apps/mcp/__tests__/pro-tool-call-gate.test.ts",
|
|
2136
|
+
"apps/mcp/__tests__/write-tool-guard.test.ts",
|
|
2137
|
+
"apps/api/src/services/__tests__/aeo-score.test.ts",
|
|
2138
|
+
"apps/api/src/routes/leads/__tests__/index.test.ts",
|
|
2139
|
+
"apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts",
|
|
2140
|
+
"apps/api/src/__tests__/server.test.ts",
|
|
2141
|
+
"apps/api/src/__tests__/cors.test.ts",
|
|
2142
|
+
"apps/api/src/__tests__/root-modules.test.ts",
|
|
2143
|
+
"apps/api/src/__tests__/module-exclusion.test.ts",
|
|
2144
|
+
"apps/api/src/openapi/__tests__/surface-drift.test.ts",
|
|
2145
|
+
"apps/api/src/__tests__/review-surface-registration.test.ts",
|
|
2146
|
+
"apps/api/src/__tests__/workspace-module-registration.test.ts",
|
|
2147
|
+
"apps/mcp/__tests__/workspace-surface.test.ts",
|
|
2148
|
+
"apps/mcp/__tests__/parity.test.ts",
|
|
2149
|
+
"apps/mcp/__tests__/audit.test.ts",
|
|
2150
|
+
"database/__tests__/build.test.ts",
|
|
2026
2151
|
];
|
|
2027
2152
|
export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
|
|
2028
2153
|
const roots = new Set(snapshotRoots);
|
package/dist/programmatic.js
CHANGED
|
@@ -45,7 +45,7 @@ import { SITE_FONTS_PATH, renderSiteFonts } from "./site-fonts.js";
|
|
|
45
45
|
import { planSiteManifest } from "./site-manifest.js";
|
|
46
46
|
import { SITE_ROSTER_PATH, planHomeRecord, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
47
47
|
import { spokeCheckoutSite, spokeEnvRowOptions, writeSpokeCheckoutSlice, } from "./spoke-checkout.js";
|
|
48
|
-
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
48
|
+
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, isUnservedSurfaceContent, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
49
49
|
import { syncRoutes } from "./sync-routes.js";
|
|
50
50
|
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
51
51
|
import { SPOKE_ONLY_FILES, TopologyError, applySpokeTransforms, isSpokeExcluded, normalizeHubSignInUrl, normalizeHubUrl, readSiteTopology, spokeExclusions, spokeDestination, spokeModuleRefusal, spokeModules, writeHubOriginEnv, writeHubSignInEnv, } from "./topology.js";
|
|
@@ -573,7 +573,14 @@ export async function scaffold(opts) {
|
|
|
573
573
|
versions,
|
|
574
574
|
markers,
|
|
575
575
|
ownership,
|
|
576
|
+
// #8427 D4 — a module bundle never writes a file inside the content
|
|
577
|
+
// directory of a gateable surface the result does not enable (the
|
|
578
|
+
// signal-collectors `projects`-surface seeds, on a scaffold whose
|
|
579
|
+
// roster carries no /projects route). The spoke filter still drops
|
|
580
|
+
// whatever it already drops; remapDestination is unchanged.
|
|
576
581
|
...spokeSkip,
|
|
582
|
+
skip: (destination) => spokeSkip.skip?.(destination) === true ||
|
|
583
|
+
isUnservedSurfaceContent(destination, result.surfaces),
|
|
577
584
|
});
|
|
578
585
|
}
|
|
579
586
|
// The template lands LAST — after `scaffoldSync` and after every module
|
|
@@ -747,11 +754,14 @@ export async function scaffold(opts) {
|
|
|
747
754
|
// two return pages, once every layer has landed (so the spoke filter has
|
|
748
755
|
// already dropped the hub-checkout files) and before the manifest, whose
|
|
749
756
|
// code-route rows then record /checkout/thank-you and /checkout/unavailable.
|
|
757
|
+
// #8217 D8 — a spoke that signs in through its hub also gets the billing
|
|
758
|
+
// action and the web seam render that reaches it.
|
|
750
759
|
const spokeCommerce = spoke !== undefined && result.modules.includes("commerce");
|
|
751
760
|
if (spokeCommerce) {
|
|
752
761
|
writeSpokeCheckoutSlice(result.targetDir, {
|
|
753
762
|
site: spokeCheckoutSite(projectName),
|
|
754
763
|
versions,
|
|
764
|
+
signIn: spoke.signIn,
|
|
755
765
|
});
|
|
756
766
|
}
|
|
757
767
|
// #6944 — the manifest records what this scaffold IS, once every layer has
|
package/dist/site-roster.js
CHANGED
|
@@ -33,6 +33,7 @@ import { locatePageSections } from "./design-presets.js";
|
|
|
33
33
|
import { diffTwoStrings } from "./lib/extract-module-bundle.js";
|
|
34
34
|
import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
|
|
35
35
|
import { substituteMarkers } from "./substitute.js";
|
|
36
|
+
import { isUnservedSurfaceContent, resolveSurfaces } from "./surfaces.js";
|
|
36
37
|
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, resolveTemplate, rosterDeclares, shippedManifestPath, templateTreeDir, } from "./template-manifest.js";
|
|
37
38
|
/** The site-root file that is the roster of record (#6658 D7). */
|
|
38
39
|
export const SITE_ROSTER_PATH = ".ailk-site-manifest.json";
|
|
@@ -613,7 +614,7 @@ function spliceRows(file, rows) {
|
|
|
613
614
|
return text.slice(0, open + 1) + body + text.slice(close);
|
|
614
615
|
}
|
|
615
616
|
/** Every `content/en/**.mdx` some layer installs into a site of this template and module set. */
|
|
616
|
-
function layerInstalledSeeds(template, modules, opts) {
|
|
617
|
+
function layerInstalledSeeds(template, modules, opts, surfaces) {
|
|
617
618
|
const out = new Set();
|
|
618
619
|
for (const root of [
|
|
619
620
|
opts.templatesDir,
|
|
@@ -640,7 +641,15 @@ function layerInstalledSeeds(template, modules, opts) {
|
|
|
640
641
|
for (const path of row.content ?? [])
|
|
641
642
|
out.add(path);
|
|
642
643
|
}
|
|
643
|
-
|
|
644
|
+
// #8427 D4 — a module installMap destination the scaffold's own
|
|
645
|
+
// surfaces do not serve is not "installed": the writer
|
|
646
|
+
// (programmatic.ts's module-bundle loop, add-module.ts) declines the
|
|
647
|
+
// write, so the reader must agree or report a seed that is not there.
|
|
648
|
+
for (const dest of Object.values(slice.installMap ?? {})) {
|
|
649
|
+
if (isUnservedSurfaceContent(dest, surfaces))
|
|
650
|
+
continue;
|
|
651
|
+
destinations.push(dest);
|
|
652
|
+
}
|
|
644
653
|
}
|
|
645
654
|
for (const to of destinations) {
|
|
646
655
|
if (to.startsWith("content/en/") && to.endsWith(".mdx"))
|
|
@@ -666,7 +675,15 @@ export function siteRosterProblems(opts) {
|
|
|
666
675
|
const roster = readSiteRoster(opts.targetDir);
|
|
667
676
|
if (roster === undefined)
|
|
668
677
|
return [];
|
|
669
|
-
const { modules } = readArchitectureHeader(readFileSync(join(opts.targetDir, "architecture.yaml"), "utf8"));
|
|
678
|
+
const { modules, surfaces: headerSurfaces } = readArchitectureHeader(readFileSync(join(opts.targetDir, "architecture.yaml"), "utf8"));
|
|
679
|
+
// #8427 D4 — the FULL effective surface set (module-contributed surfaces
|
|
680
|
+
// unioned with the header's own explicit list), the same set the writer
|
|
681
|
+
// resolves. Throws SurfaceError on a header surface that is unknown or
|
|
682
|
+
// owned by a module the site lacks — intended: `add-module` already
|
|
683
|
+
// refuses such a header before its first write, so a direct caller
|
|
684
|
+
// reaches the same refusal instead of a report built on a header the
|
|
685
|
+
// scaffold would never actually write.
|
|
686
|
+
const surfaces = resolveSurfaces(modules, headerSurfaces);
|
|
670
687
|
const problems = [];
|
|
671
688
|
const routes = roster.rows.map((r) => r.value["route"]);
|
|
672
689
|
const seen = new Set();
|
|
@@ -715,7 +732,7 @@ export function siteRosterProblems(opts) {
|
|
|
715
732
|
});
|
|
716
733
|
// #6942 — the Free /meet placeholder seeds the scheduling module's rows keep.
|
|
717
734
|
const declared = withMeetPlaceholderSeeds(effective);
|
|
718
|
-
const installed = [...layerInstalledSeeds(roster.template, modules, opts)]
|
|
735
|
+
const installed = [...layerInstalledSeeds(roster.template, modules, opts, surfaces)]
|
|
719
736
|
.filter((path) => existsSync(join(opts.targetDir, path)))
|
|
720
737
|
.sort();
|
|
721
738
|
for (const path of installed) {
|
package/dist/spoke-checkout.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EnvCatalogEntry, PlanEnvRowsOptions } from "./lib/declared-env.js";
|
|
2
2
|
import { type InstallReportRow } from "./lib/extract-module-bundle.js";
|
|
3
3
|
import { type WorkspaceVersionMap } from "./substitute.js";
|
|
4
|
+
import { type SignInMode } from "./topology.js";
|
|
4
5
|
/** The checkout package a spoke's two route files import. */
|
|
5
6
|
export declare const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
|
|
6
7
|
/** The site's checkout data — written once, the site's from then on. */
|
|
@@ -48,13 +49,19 @@ export declare const CHECKOUT_E2E_WORKFLOW: string;
|
|
|
48
49
|
/**
|
|
49
50
|
* The slice for `site`, in order: the two route files, the config, the two
|
|
50
51
|
* pages (D3), then the checkout e2e workflow (#7839 D8), which does not
|
|
51
|
-
* depend on `site`.
|
|
52
|
+
* depend on `site`. With `signIn: "hub"` the billing action is the third
|
|
53
|
+
* pair, so the first three paths are `SPOKE_CHECKOUT_ROUTE_FILES` (#8217 D8).
|
|
54
|
+
* Throws `TopologyError` for an invalid stamp (SEC-7838-10).
|
|
52
55
|
*/
|
|
53
|
-
export declare function renderSpokeCheckoutFiles(site: string
|
|
56
|
+
export declare function renderSpokeCheckoutFiles(site: string, opts?: {
|
|
57
|
+
signIn?: SignInMode | undefined;
|
|
58
|
+
}): [string, string][];
|
|
54
59
|
/**
|
|
55
60
|
* Write the slice into `targetDir` (D3). A file that already exists is left
|
|
56
61
|
* byte-for-byte and reported `skipped` — the config is the site's once
|
|
57
|
-
* written.
|
|
62
|
+
* written. With `signIn: "hub"` (#8217 D8) the slice carries the billing
|
|
63
|
+
* action, and the web seam — generated, so always rewritten — becomes the
|
|
64
|
+
* billing render: reported `patched` when it changed, not at all when equal. Adds the package to `apps/web/package.json` when absent, and
|
|
58
65
|
* raises a plain pin older than the one it writes (#7839): the generated
|
|
59
66
|
* checkout e2e workflow runs a command a `^0.2.0` pin never reaches. Any
|
|
60
67
|
* other range — newer, `workspace:`, a tag — is the site's and is kept.
|
|
@@ -64,4 +71,5 @@ export declare function renderSpokeCheckoutFiles(site: string): [string, string]
|
|
|
64
71
|
export declare function writeSpokeCheckoutSlice(targetDir: string, opts: {
|
|
65
72
|
site: string;
|
|
66
73
|
versions: WorkspaceVersionMap;
|
|
74
|
+
signIn?: SignInMode | undefined;
|
|
67
75
|
}): InstallReportRow[];
|
package/dist/spoke-checkout.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The spoke checkout slice (#7838) — what a spoke carrying `commerce` gets in
|
|
3
|
-
* `apps/web`: the buy route and the Stripe webhook route (
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* `apps/web`: the buy route and the Stripe webhook route (allowed to import
|
|
4
|
+
* the checkout package, #7873), the site's own `lib/checkout-config.ts`, and
|
|
5
|
+
* the thank-you and unavailable return pages. A spoke that signs in through
|
|
6
|
+
* its hub also gets the billing action, the third file allowed to import the
|
|
7
|
+
* package, and the web seam render that reaches it (#8217 D6–D8).
|
|
6
8
|
*
|
|
7
9
|
* Every guarantee lives in `@working-theory/spoke-checkout` and arrives by a
|
|
8
10
|
* package bump; these files are thin and, apart from the config's one stamp
|
|
@@ -23,7 +25,7 @@ import { SPOKE_CHECKOUT_ROUTE_FILES } from "./derive-architecture.js";
|
|
|
23
25
|
import { ENV_CATALOG } from "./lib/env-catalog.js";
|
|
24
26
|
import { diffTwoStrings, } from "./lib/extract-module-bundle.js";
|
|
25
27
|
import { substituteWorkspaceVersions, } from "./substitute.js";
|
|
26
|
-
import { TopologyError } from "./topology.js";
|
|
28
|
+
import { TopologyError, WEB_SEAM_PATH, renderWebSeam, } from "./topology.js";
|
|
27
29
|
/** The checkout package a spoke's two route files import. */
|
|
28
30
|
export const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
|
|
29
31
|
const API_CLIENT_PACKAGE = "@working-theory/api-client";
|
|
@@ -131,6 +133,33 @@ const WEBHOOK_ROUTE = renderRoute({
|
|
|
131
133
|
waiver: "the endpoint URL registered in Stripe, not a versioned API contract",
|
|
132
134
|
exportLine: "export const { POST } = createSpokeCheckout(checkoutConfig).webhook;",
|
|
133
135
|
});
|
|
136
|
+
/**
|
|
137
|
+
* The spoke billing action (#8217 D6): wiring only, byte-identical across
|
|
138
|
+
* sites, so every guarantee arrives by a package bump. A plain server module
|
|
139
|
+
* (C2): its two exports take no argument, and the hub's read is its only
|
|
140
|
+
* source of customer ids.
|
|
141
|
+
*/
|
|
142
|
+
const BILLING_ACTION = [
|
|
143
|
+
"// The spoke billing action — GENERATED by create-ailk (#8217). Do not edit:",
|
|
144
|
+
"// every guarantee lives in the spoke-checkout package and arrives by a",
|
|
145
|
+
"// package bump. A plain server module, not a Server Action file: neither",
|
|
146
|
+
"// export takes an argument, and the hub's GET /v1/member/billing is the only",
|
|
147
|
+
"// source of customer ids. billing-source.ts reaches it through",
|
|
148
|
+
"// lib/topology.generated.ts and never imports the package.",
|
|
149
|
+
`import { member } from "${API_CLIENT_PACKAGE}";`,
|
|
150
|
+
`import { createSpokeCheckout } from "${SPOKE_CHECKOUT_PACKAGE}";`,
|
|
151
|
+
"",
|
|
152
|
+
`import { checkoutConfig } from "../../../../../lib/checkout-config";`,
|
|
153
|
+
`import { forwardedCookieInit } from "../../../../../lib/server-api";`,
|
|
154
|
+
"",
|
|
155
|
+
"const billing = createSpokeCheckout(checkoutConfig).billing.forMember(",
|
|
156
|
+
" async () => member.billing(await forwardedCookieInit()),",
|
|
157
|
+
");",
|
|
158
|
+
"",
|
|
159
|
+
"export const readSpokeBilling = billing.read;",
|
|
160
|
+
"export const spokeBillingPortalUrl = billing.portalUrl;",
|
|
161
|
+
"",
|
|
162
|
+
].join("\n");
|
|
134
163
|
/** The site's checkout data (D5). Only the `const SITE` line varies by site. */
|
|
135
164
|
function renderConfig(site) {
|
|
136
165
|
return [
|
|
@@ -138,9 +167,10 @@ function renderConfig(site) {
|
|
|
138
167
|
"//",
|
|
139
168
|
"// Written once by create-ailk and yours from then on: create-ailk never",
|
|
140
169
|
"// rewrites it. The buy route and the Stripe webhook route build their",
|
|
141
|
-
"// handlers from it
|
|
142
|
-
"//
|
|
143
|
-
"//
|
|
170
|
+
"// handlers from it, and so does the billing action on a spoke that signs",
|
|
171
|
+
"// in through its hub. Only those files may import the checkout package",
|
|
172
|
+
"// (architecture.yaml), so this file never imports it, stripe-client or the",
|
|
173
|
+
"// stripe SDK. Keep it to data and the fulfil callback.",
|
|
144
174
|
"//",
|
|
145
175
|
"// site Stamped on every Checkout Session as metadata.site. This site's",
|
|
146
176
|
"// webhook acts only on its own stamp, and the hub ignores any stamp",
|
|
@@ -462,15 +492,20 @@ jobs:
|
|
|
462
492
|
/**
|
|
463
493
|
* The slice for `site`, in order: the two route files, the config, the two
|
|
464
494
|
* pages (D3), then the checkout e2e workflow (#7839 D8), which does not
|
|
465
|
-
* depend on `site`.
|
|
495
|
+
* depend on `site`. With `signIn: "hub"` the billing action is the third
|
|
496
|
+
* pair, so the first three paths are `SPOKE_CHECKOUT_ROUTE_FILES` (#8217 D8).
|
|
497
|
+
* Throws `TopologyError` for an invalid stamp (SEC-7838-10).
|
|
466
498
|
*/
|
|
467
|
-
export function renderSpokeCheckoutFiles(site) {
|
|
499
|
+
export function renderSpokeCheckoutFiles(site, opts = {}) {
|
|
468
500
|
assertSite(site);
|
|
469
|
-
const [buyPath, webhookPath] = SPOKE_CHECKOUT_ROUTE_FILES;
|
|
501
|
+
const [buyPath, webhookPath, billingActionPath] = SPOKE_CHECKOUT_ROUTE_FILES;
|
|
470
502
|
const [thankYouPath, unavailablePath] = SPOKE_CHECKOUT_PAGE_FILES;
|
|
471
503
|
return [
|
|
472
504
|
[buyPath, BUY_ROUTE],
|
|
473
505
|
[webhookPath, WEBHOOK_ROUTE],
|
|
506
|
+
...(opts.signIn === "hub"
|
|
507
|
+
? [[billingActionPath, BILLING_ACTION]]
|
|
508
|
+
: []),
|
|
474
509
|
[SPOKE_CHECKOUT_CONFIG_PATH, renderConfig(site)],
|
|
475
510
|
[thankYouPath, THANK_YOU_PAGE],
|
|
476
511
|
[unavailablePath, UNAVAILABLE_PAGE],
|
|
@@ -516,7 +551,9 @@ function isOlderPin(current, wanted) {
|
|
|
516
551
|
/**
|
|
517
552
|
* Write the slice into `targetDir` (D3). A file that already exists is left
|
|
518
553
|
* byte-for-byte and reported `skipped` — the config is the site's once
|
|
519
|
-
* written.
|
|
554
|
+
* written. With `signIn: "hub"` (#8217 D8) the slice carries the billing
|
|
555
|
+
* action, and the web seam — generated, so always rewritten — becomes the
|
|
556
|
+
* billing render: reported `patched` when it changed, not at all when equal. Adds the package to `apps/web/package.json` when absent, and
|
|
520
557
|
* raises a plain pin older than the one it writes (#7839): the generated
|
|
521
558
|
* checkout e2e workflow runs a command a `^0.2.0` pin never reaches. Any
|
|
522
559
|
* other range — newer, `workspace:`, a tag — is the site's and is kept.
|
|
@@ -524,7 +561,7 @@ function isOlderPin(current, wanted) {
|
|
|
524
561
|
* missing or malformed `apps/web/package.json`) throws before the first write.
|
|
525
562
|
*/
|
|
526
563
|
export function writeSpokeCheckoutSlice(targetDir, opts) {
|
|
527
|
-
const files = renderSpokeCheckoutFiles(opts.site);
|
|
564
|
+
const files = renderSpokeCheckoutFiles(opts.site, { signIn: opts.signIn });
|
|
528
565
|
const range = JSON.parse(substituteWorkspaceVersions(`{"${SPOKE_CHECKOUT_PACKAGE}": "workspace:*"}`, opts.versions));
|
|
529
566
|
const version = range[SPOKE_CHECKOUT_PACKAGE];
|
|
530
567
|
const pkgPath = join(targetDir, WEB_PACKAGE_JSON);
|
|
@@ -541,6 +578,19 @@ export function writeSpokeCheckoutSlice(targetDir, opts) {
|
|
|
541
578
|
writeFileSync(abs, text);
|
|
542
579
|
rows.push({ path, action: "written" });
|
|
543
580
|
}
|
|
581
|
+
if (opts.signIn === "hub") {
|
|
582
|
+
const seamPath = join(targetDir, WEB_SEAM_PATH);
|
|
583
|
+
const seamBefore = existsSync(seamPath) ? readFileSync(seamPath, "utf8") : "";
|
|
584
|
+
const seam = renderWebSeam("spoke", "hub", { spokeBilling: true });
|
|
585
|
+
if (seamBefore !== seam) {
|
|
586
|
+
writeFileSync(seamPath, seam);
|
|
587
|
+
rows.push({
|
|
588
|
+
path: WEB_SEAM_PATH,
|
|
589
|
+
action: "patched",
|
|
590
|
+
change: diffTwoStrings(WEB_SEAM_PATH, seamBefore, seam, true),
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
544
594
|
const deps = pkg.dependencies ?? {};
|
|
545
595
|
const current = Object.prototype.hasOwnProperty.call(deps, SPOKE_CHECKOUT_PACKAGE)
|
|
546
596
|
? deps[SPOKE_CHECKOUT_PACKAGE]
|
package/dist/starter-grid.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* lives in `template-manifest.ts`, whose `resolveTemplate` is the one reader
|
|
15
15
|
* that yields a template's roster. Nothing but those two files imports this one.
|
|
16
16
|
*/
|
|
17
|
-
import type { CategorySlug } from "
|
|
17
|
+
import type { CategorySlug } from "./category-profile.js";
|
|
18
18
|
import type { ModuleName } from "./parse-args.js";
|
|
19
19
|
import type { TemplateLayout } from "./site-chrome.js";
|
|
20
20
|
import type { StarterDesign, StarterShape, TemplateRosterRow } from "./template-manifest.js";
|
package/dist/surfaces.d.ts
CHANGED
|
@@ -126,6 +126,20 @@ export declare function excludedRoutePaths(enabled: string[]): string[];
|
|
|
126
126
|
* route has been removed would advertise URLs that 404.
|
|
127
127
|
*/
|
|
128
128
|
export declare function excludedContentPaths(enabled: string[]): string[];
|
|
129
|
+
/**
|
|
130
|
+
* True when `path` (scaffold-relative, `/`-separated) lies inside one of the
|
|
131
|
+
* content directories `excludedContentPaths(enabled)` names, with `<locale>`
|
|
132
|
+
* matching any one path segment (#8427 D4).
|
|
133
|
+
*
|
|
134
|
+
* This is `excludedContentPaths`' first non-export reader — its own comment
|
|
135
|
+
* already named exactly this class ("orphan content makes `/sitemap.xml`
|
|
136
|
+
* advertise URLs that 404"). It is wired into the `skip` predicate
|
|
137
|
+
* `extractModuleBundle` already takes (`programmatic.ts`, `add-module.ts`),
|
|
138
|
+
* and into `layerInstalledSeeds`'s installed-seed count (`site-roster.ts`),
|
|
139
|
+
* so a module bundle never writes — and a roster check never counts — a
|
|
140
|
+
* seed under a gateable surface the scaffold does not enable.
|
|
141
|
+
*/
|
|
142
|
+
export declare function isUnservedSurfaceContent(path: string, enabled: readonly string[]): boolean;
|
|
129
143
|
/**
|
|
130
144
|
* Route manifest (#4986): every template route directory, keyed relative to
|
|
131
145
|
* ROUTE_ROOT (`apps/web/app/[locale]`), → the `@working-theory/*` fixed-group
|