create-ailk 0.22.0 → 0.23.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.json +149 -5
- package/component-catalog.md +7 -6
- package/dist/add-module.js +11 -9
- package/dist/add-route.d.ts +55 -1
- package/dist/add-route.js +294 -12
- package/dist/cli.js +27 -10
- package/dist/derive-architecture.d.ts +7 -5
- package/dist/derive-architecture.js +9 -32
- package/dist/lib/design-checks.js +4 -2
- package/dist/lib/env-catalog.js +0 -4
- package/dist/lib/npm-auth.d.ts +9 -0
- package/dist/lib/npm-auth.js +48 -0
- package/dist/lib/scaffold-render-sweep.d.ts +3 -2
- package/dist/lib/scaffold-render-sweep.js +5 -8
- package/dist/module-architecture.js +98 -10
- package/dist/paid-sections.d.ts +21 -2
- package/dist/paid-sections.js +69 -7
- package/dist/seed-flows.d.ts +4 -1
- package/dist/seed-flows.js +50 -4
- package/dist/spoke-checkout.d.ts +38 -35
- package/dist/spoke-checkout.js +66 -229
- package/dist/starter-grid.d.ts +0 -3
- package/dist/starter-grid.js +24 -14
- package/dist/surfaces.js +33 -12
- package/dist/sync-routes.d.ts +38 -1
- package/dist/sync-routes.js +184 -11
- package/dist/template-record.d.ts +1 -1
- package/dist/template-record.js +3 -2
- package/dist/update.js +10 -0
- package/package.json +3 -3
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +4 -49
- package/site-templates/_base/content/en/Pricing/pricing.mdx +3 -21
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +23 -20
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +28 -14
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +15 -39
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +38 -34
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +14 -31
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +26 -25
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +6 -43
- package/templates/.env.example +9 -7
- package/templates/.github/workflows/ci.yml +13 -0
- package/templates/README.md +1 -0
- package/templates/_gitignore +4 -0
- package/templates/apps/api/.env.example +27 -0
- package/templates/apps/api/CLAUDE.md +77 -16
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +2 -0
- package/templates/apps/api/src/bin/stripe-connections-rekey.ts +45 -0
- package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +185 -0
- package/templates/apps/api/src/config/index.ts +109 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
- package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +116 -0
- package/templates/apps/api/src/lib/error-serializer.ts +74 -7
- package/templates/apps/api/src/lib/stripe.ts +74 -4
- package/templates/apps/api/src/lib/webhook-idempotency.ts +66 -2
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +218 -0
- package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +174 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +226 -4
- package/templates/apps/api/src/openapi/spec.ts +10 -7
- package/templates/apps/api/src/routes/billing/plan-checkout.ts +325 -0
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +1300 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +11 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +49 -0
- package/templates/apps/api/src/routes/checkout/session.ts +57 -25
- package/templates/apps/api/src/routes/checkout/sessions.ts +232 -82
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +5 -5
- package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +160 -0
- package/templates/apps/api/src/routes/consent/account.ts +4 -2
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +2 -2
- package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +731 -0
- package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +753 -0
- package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +762 -0
- package/templates/apps/api/src/routes/content/index.ts +16 -2
- package/templates/apps/api/src/routes/content/offerings.ts +184 -0
- package/templates/apps/api/src/routes/content/pricing.ts +176 -0
- package/templates/apps/api/src/routes/content/shown-offerings.ts +229 -0
- package/templates/apps/api/src/routes/leads/index.ts +17 -14
- package/templates/apps/api/src/routes/webhooks/README.md +66 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +814 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +80 -68
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +5 -5
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +8 -2
- package/templates/apps/api/src/routes/webhooks/index.ts +23 -0
- package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +384 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +91 -17
- package/templates/apps/api/src/server.ts +140 -51
- package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +313 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +51 -2
- package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +234 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +374 -44
- package/templates/apps/api/src/services/__tests__/person.test.ts +232 -1
- package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +444 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +13 -5
- package/templates/apps/api/src/services/entitlements.ts +157 -1
- package/templates/apps/api/src/services/lead-routing.ts +113 -58
- package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +295 -0
- package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +268 -0
- package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +868 -0
- package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +416 -0
- package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +396 -0
- package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +681 -0
- package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +231 -0
- package/templates/apps/api/src/services/offerings/catalog.ts +308 -0
- package/templates/apps/api/src/services/offerings/checkout-authorization.ts +385 -0
- package/templates/apps/api/src/services/offerings/checkout-session-record.ts +192 -0
- package/templates/apps/api/src/services/offerings/connection.ts +1003 -0
- package/templates/apps/api/src/services/offerings/index.ts +614 -0
- package/templates/apps/api/src/services/offerings/source.ts +140 -0
- package/templates/apps/api/src/services/offerings/store.ts +150 -0
- package/templates/apps/api/src/services/person.ts +124 -9
- package/templates/apps/api/src/services/plan-offerings.ts +96 -30
- package/templates/apps/api/src/services/public-site-resolver.ts +19 -14
- package/templates/apps/mcp/CLAUDE.md +22 -14
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +9 -4
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +358 -0
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +364 -0
- package/templates/apps/mcp/__tests__/http-transport.test.ts +4 -4
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +2 -2
- package/templates/apps/mcp/__tests__/server.test.ts +13 -11
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +208 -12
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +11 -11
- package/templates/apps/mcp/src/lib/shown-offerings.ts +105 -0
- package/templates/apps/mcp/src/server.ts +5 -0
- package/templates/apps/mcp/src/tools/get_offerings.ts +47 -30
- package/templates/apps/mcp/src/tools/get_pricing.ts +40 -40
- package/templates/apps/mcp/src/tools/purchase.ts +30 -2
- package/templates/apps/mcp/src/workspace-server.ts +15 -0
- package/templates/apps/web/.env.example +10 -31
- package/templates/apps/web/CLAUDE.md +315 -42
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/careers/[slug]/[action]/page.tsx +189 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +4 -2
- package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +8 -3
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +22 -144
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +9 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-hub.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/careers-set.test.tsx +149 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +22 -24
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +140 -66
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +57 -22
- package/templates/apps/web/app/[locale]/careers/page.tsx +129 -5
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +119 -11
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +28 -5
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +6 -2
- package/templates/apps/web/app/[locale]/contact/page.tsx +99 -92
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +237 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.count.test.tsx +43 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.deliverable.test.tsx +200 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.ending.test.tsx +197 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +430 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +21 -9
- package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +352 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +12 -73
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +10 -31
- package/templates/apps/web/app/[locale]/layout.tsx +55 -4
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +174 -104
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +95 -58
- package/templates/apps/web/app/[locale]/pricing/__tests__/__snapshots__/unconnected.test.tsx.snap +7 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +599 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +216 -0
- package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +236 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +367 -6
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +4 -1
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +17 -0
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +62 -3
- package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +70 -18
- package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +30 -9
- package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +316 -0
- package/templates/apps/web/app/api/offerings/checkout/__tests__/route.test.ts +810 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +27 -1
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +2 -1
- package/templates/apps/web/lib/__tests__/in-page-magnet.test.ts +265 -0
- package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +57 -0
- package/templates/apps/web/lib/__tests__/newsletter.actions.test.ts +293 -0
- package/templates/apps/web/lib/__tests__/offerings-pricing.test.ts +601 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +67 -8
- package/templates/apps/web/lib/auth-routes.ts +36 -2
- package/templates/apps/web/lib/checkout-config.ts +9 -20
- package/templates/apps/web/lib/deck-page.ts +1 -0
- package/templates/apps/web/lib/deliverable-grant.ts +18 -0
- package/templates/apps/web/lib/in-page-magnet.ts +416 -0
- package/templates/apps/web/lib/magnet-routes.ts +42 -0
- package/templates/apps/web/lib/modules.generated.ts +9 -0
- package/templates/apps/web/lib/newsletter.actions.ts +202 -0
- package/templates/apps/web/lib/offerings-pricing.ts +326 -0
- package/templates/apps/web/lib/posting-title.ts +17 -0
- package/templates/apps/web/lib/sign-up-agreements.ts +4 -2
- package/templates/apps/web/messages/en.json +49 -15
- package/templates/apps/web/middleware.ts +12 -2
- package/templates/apps/web/security-headers.config.ts +17 -0
- package/templates/content/en/BlogList/blog.mdx +4 -71
- package/templates/content/en/BookCall/book-a-call.mdx +1 -41
- package/templates/content/en/BookCall/onsite-estimate.mdx +1 -17
- package/templates/content/en/BookCall/onsite-service.mdx +1 -17
- package/templates/content/en/BookCall/start-project.mdx +1 -17
- package/templates/content/en/ContactPage/contact.mdx +6 -63
- package/templates/content/en/HomePage/home.mdx +3 -21
- package/templates/database/CHANGELOG.md +31 -0
- package/templates/database/__tests__/in-page-magnets-migration.test.ts +161 -0
- package/templates/database/__tests__/offerings-checkout-session.db.test.ts +270 -0
- package/templates/database/__tests__/offerings-migration.test.ts +197 -0
- package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +86 -0
- package/templates/database/__tests__/offerings.db.test.ts +303 -0
- package/templates/database/__tests__/seed.test.ts +31 -1
- package/templates/database/auth/schema.prisma +4 -0
- package/templates/database/config/schema.prisma +91 -0
- package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +146 -0
- package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +77 -0
- package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +45 -0
- package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +47 -0
- package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +16 -0
- package/templates/database/package.json +1 -1
- package/templates/database/seed.ts +30 -2
- package/versions.json +35 -35
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -199
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +0 -93
- package/templates/apps/web/app/[locale]/downloads/page.tsx +0 -37
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +0 -139
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +0 -93
- package/templates/apps/web/app/api/checkout/[offering]/route.ts +0 -48
- package/templates/apps/web/app/api/checkout/__tests__/route.test.ts +0 -120
- package/templates/apps/web/app/api/checkout/__tests__/success-path.test.ts +0 -86
- package/templates/apps/web/app/api/webhooks/stripe/__tests__/route.test.ts +0 -92
- package/templates/apps/web/app/api/webhooks/stripe/route.ts +0 -31
- package/templates/apps/web/lib/__tests__/checkout.test.ts +0 -243
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +0 -120
- package/templates/apps/web/lib/checkout.ts +0 -93
- package/templates/apps/web/lib/meeting-slugs.ts +0 -63
package/dist/spoke-checkout.d.ts
CHANGED
|
@@ -2,26 +2,26 @@ 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
4
|
import { type SignInMode } from "./topology.js";
|
|
5
|
-
/** The checkout package a spoke's
|
|
5
|
+
/** The checkout package a spoke's billing action and thank-you page import. */
|
|
6
6
|
export declare const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
|
|
7
|
-
/** The site's checkout
|
|
7
|
+
/** The site's checkout stamp — written once, the site's from then on (#8748 D2). */
|
|
8
8
|
export declare const SPOKE_CHECKOUT_CONFIG_PATH = "apps/web/lib/checkout-config.ts";
|
|
9
|
-
/** The two return pages
|
|
9
|
+
/** The two return pages a commerce spoke still carries (#8748 D5). */
|
|
10
10
|
export declare const SPOKE_CHECKOUT_PAGE_FILES: readonly string[];
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
* the
|
|
15
|
-
*
|
|
12
|
+
* The spoke checkout's template paths, scaffold-relative (#8748 D3). A
|
|
13
|
+
* directory entry covers every file under it, so the tests that sit beside
|
|
14
|
+
* the pages travel with them. `copyTemplate` skips all of them for every
|
|
15
|
+
* scaffold; `writeSpokeCheckoutSlice` copies them into a commerce spoke.
|
|
16
16
|
*/
|
|
17
17
|
export declare const SPOKE_CHECKOUT_TEMPLATE_PATHS: readonly string[];
|
|
18
|
-
/** The variables a commerce spoke depends on (D9)
|
|
18
|
+
/** The variables a commerce spoke depends on (D9): the package reads both. */
|
|
19
19
|
export declare const SPOKE_CHECKOUT_ENV_CATALOG: Readonly<Record<string, EnvCatalogEntry>>;
|
|
20
20
|
/** The names a commerce spoke declares although only the package reads them (D9). */
|
|
21
21
|
export declare const SPOKE_CHECKOUT_DECLARED_ENV: readonly string[];
|
|
22
22
|
/**
|
|
23
23
|
* The catalog (and declared names) a spoke's `appendEnvRows` call passes: a
|
|
24
|
-
* commerce spoke adds the
|
|
24
|
+
* commerce spoke adds the package's two rows (D9). `ENV_CATALOG` itself is
|
|
25
25
|
* unchanged — its test fails on an entry the monorepo's apps never read.
|
|
26
26
|
*/
|
|
27
27
|
export declare function spokeEnvRowOptions(commerce: boolean): Pick<PlanEnvRowsOptions, "catalog" | "declare">;
|
|
@@ -32,33 +32,22 @@ export declare function spokeEnvRowOptions(commerce: boolean): Pick<PlanEnvRowsO
|
|
|
32
32
|
* `site-` and the first 8 hex characters of its SHA-256. Never `AILK_SITE_ID`.
|
|
33
33
|
*/
|
|
34
34
|
export declare function spokeCheckoutSite(projectName: string): string;
|
|
35
|
-
/**
|
|
36
|
-
export declare const
|
|
35
|
+
/** The billing action's first line, which marks the file as create-ailk's to rewrite. */
|
|
36
|
+
export declare const BILLING_ACTION_MARKER = "// The spoke billing action \u2014 GENERATED by create-ailk (#8217). Do not edit:";
|
|
37
37
|
/**
|
|
38
|
-
* The
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* The spoke billing action (#8217 D6): wiring only, byte-identical across
|
|
39
|
+
* sites, so every guarantee arrives by a package bump. A plain server module
|
|
40
|
+
* (C2): its two exports take no argument, and the hub's read is its only
|
|
41
|
+
* source of customer ids. It builds its config from the site's stamp and the
|
|
42
|
+
* site's own origin (#9142: no longer from `lib/checkout.ts`).
|
|
41
43
|
*/
|
|
42
|
-
export declare const
|
|
43
|
-
export declare const STRIPE_CLI_LINUX_X64_SHA256 = "38c8de73a8cab810a4aab8a4015cd4e3398b7b87aa42b6944d09d89ed524ac53";
|
|
44
|
-
/** The Playwright the workflow installs — the monorepo's own pinned version. */
|
|
45
|
-
export declare const E2E_PLAYWRIGHT_VERSION = "1.61.1";
|
|
46
|
-
/**
|
|
47
|
-
* Every action the workflow uses, pinned to the commit its `v4` tag named
|
|
48
|
-
* when this was written (SEC-7839-2): `gh api repos/<action>/commits/v4`.
|
|
49
|
-
*/
|
|
50
|
-
export declare const E2E_ACTION_PINS: Readonly<Record<string, string>>;
|
|
51
|
-
/**
|
|
52
|
-
* The workflow text (D8). Identical for every spoke at one create-ailk
|
|
53
|
-
* version. Every `${{ … }}` sits in an env-style value, never in a script.
|
|
54
|
-
*/
|
|
55
|
-
export declare const CHECKOUT_E2E_WORKFLOW: string;
|
|
44
|
+
export declare const BILLING_ACTION: string;
|
|
56
45
|
/**
|
|
57
46
|
* The rendered part of the slice for `site` (#8748 D3): with `signIn: "hub"`
|
|
58
|
-
* the billing action (#8217 D8),
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
47
|
+
* the billing action (#8217 D8), else nothing. It does not depend on `site`.
|
|
48
|
+
* The rest of the slice is template source, copied by
|
|
49
|
+
* `writeSpokeCheckoutSlice`. Throws `TopologyError` for an invalid stamp
|
|
50
|
+
* (SEC-7838-10).
|
|
62
51
|
*/
|
|
63
52
|
export declare function renderSpokeCheckoutFiles(site: string, opts?: {
|
|
64
53
|
signIn?: SignInMode | undefined;
|
|
@@ -76,9 +65,10 @@ export declare function renderSpokeCheckoutFiles(site: string, opts?: {
|
|
|
76
65
|
* action, and the web seam — generated, so always rewritten — becomes the
|
|
77
66
|
* billing render: reported `patched` when it changed, not at all when equal.
|
|
78
67
|
* Adds the package to `apps/web/package.json` when absent, and raises a plain
|
|
79
|
-
* pin older than the one it writes (#7839): the
|
|
80
|
-
*
|
|
81
|
-
* newer, `workspace:`, a tag —
|
|
68
|
+
* pin older than the one it writes (#7839): the rendered billing action
|
|
69
|
+
* calls the package's current `createSpokeCheckout` shape, which an older
|
|
70
|
+
* pin does not take (#9142). Any other range — newer, `workspace:`, a tag —
|
|
71
|
+
* is the site's and is kept.
|
|
82
72
|
* Every refusal (an invalid stamp, a version map without the package, a
|
|
83
73
|
* missing or malformed `apps/web/package.json`, a template missing any
|
|
84
74
|
* `SPOKE_CHECKOUT_TEMPLATE_PATHS` entry, a template config without exactly
|
|
@@ -101,3 +91,16 @@ export declare function writeSpokeCheckoutSlice(targetDir: string, opts: {
|
|
|
101
91
|
* dependency, or without `apps/web/package.json`, is left as it is.
|
|
102
92
|
*/
|
|
103
93
|
export declare function dropSpokeCheckoutDependency(targetDir: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* What the legacy env-key checkout left in a spoke, scaffold-relative: the
|
|
96
|
+
* buy route and the Stripe webhook route (a directory entry covers the tests
|
|
97
|
+
* beside each), `lib/checkout.ts` (the fulfilment) and its test. Each was a
|
|
98
|
+
* template copy create-ailk recorded in `.ailk-template-files.json`;
|
|
99
|
+
* `create-ailk sync-routes --apply` removes the ones still byte-identical to
|
|
100
|
+
* that record.
|
|
101
|
+
*/
|
|
102
|
+
export declare const RETIRED_SPOKE_CHECKOUT_PATHS: readonly string[];
|
|
103
|
+
/** The retired checkout e2e workflow — rendered, never recorded (#7839 D8). */
|
|
104
|
+
export declare const RETIRED_CHECKOUT_E2E_WORKFLOW_PATH = ".github/workflows/checkout-e2e.yml";
|
|
105
|
+
/** The workflow's first line, which marks it as create-ailk's to remove. */
|
|
106
|
+
export declare const RETIRED_CHECKOUT_E2E_MARKER = "# Checkout e2e \u2014 GENERATED by create-ailk (#7839). Do not edit: it changes";
|
package/dist/spoke-checkout.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The spoke checkout slice (#7838, #8748) — what a spoke carrying `commerce`
|
|
3
|
-
* gets in `apps/web
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
3
|
+
* gets in `apps/web`, copied from the template tree: the site's own
|
|
4
|
+
* `lib/checkout-config.ts` (its stamp) and the thank-you and unavailable
|
|
5
|
+
* return pages with their tests (`SPOKE_CHECKOUT_TEMPLATE_PATHS`). A spoke
|
|
6
|
+
* that signs in through its hub also gets the billing action, the one file
|
|
7
|
+
* allowed to import the package besides the thank-you page, and the web seam
|
|
8
|
+
* render that reaches it (#8217 D6–D8); that action is rendered here.
|
|
9
|
+
*
|
|
10
|
+
* A spoke sells through its hub's Offerings buy route
|
|
11
|
+
* (`apps/web/app/api/offerings/checkout/[lookupKey]/route.ts`), never its own
|
|
12
|
+
* key. The legacy env-key buy route, its Stripe webhook route, `lib/checkout.ts`
|
|
13
|
+
* (the fulfilment) and the checkout e2e workflow are retired (#9142):
|
|
14
|
+
* `create-ailk sync-routes --apply` removes them from an existing spoke
|
|
15
|
+
* (`RETIRED_SPOKE_CHECKOUT_PATHS`, sync-routes.ts).
|
|
12
16
|
*
|
|
13
17
|
* Every guarantee lives in `@working-theory/spoke-checkout` and arrives by a
|
|
14
18
|
* package bump; the copied files are `apps/web` source, tested in the
|
|
15
19
|
* monorepo and kept current by `create-ailk update`, and apart from the
|
|
16
20
|
* config's one stamp line they are byte-identical across spokes at one
|
|
17
|
-
* create-ailk version (roadmap D3, AC-SPOKE-02). A hub never gets them
|
|
18
|
-
* checkout is `apps/api`'s.
|
|
21
|
+
* create-ailk version (roadmap D3, AC-SPOKE-02). A hub never gets them.
|
|
19
22
|
*
|
|
20
23
|
* Spec: docs/specs/7838-spoke-web-checkout-slice-spec.md (D3–D7, D9);
|
|
21
24
|
* docs/specs/8748-a-standard-spoke-checkout-route-spec.md (D2, D3).
|
|
@@ -30,62 +33,50 @@ import { createHash } from "node:crypto";
|
|
|
30
33
|
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync, } from "node:fs";
|
|
31
34
|
import { dirname, join, resolve, sep } from "node:path";
|
|
32
35
|
import { buildScaffoldMarkers, renderTemplateFile, templateDestination } from "./copy.js";
|
|
33
|
-
import {
|
|
36
|
+
import { SPOKE_BILLING_ACTION_PATH } from "./derive-architecture.js";
|
|
34
37
|
import { ENV_CATALOG } from "./lib/env-catalog.js";
|
|
35
38
|
import { diffTwoStrings, } from "./lib/extract-module-bundle.js";
|
|
36
39
|
import { substituteWorkspaceVersions, } from "./substitute.js";
|
|
37
40
|
import { TopologyError, WEB_SEAM_PATH, renderWebSeam, } from "./topology.js";
|
|
38
|
-
/** The checkout package a spoke's
|
|
41
|
+
/** The checkout package a spoke's billing action and thank-you page import. */
|
|
39
42
|
export const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
|
|
40
43
|
const API_CLIENT_PACKAGE = "@working-theory/api-client";
|
|
41
|
-
/** The site's checkout
|
|
44
|
+
/** The site's checkout stamp — written once, the site's from then on (#8748 D2). */
|
|
42
45
|
export const SPOKE_CHECKOUT_CONFIG_PATH = "apps/web/lib/checkout-config.ts";
|
|
43
|
-
/** The two return pages
|
|
46
|
+
/** The two return pages a commerce spoke still carries (#8748 D5). */
|
|
44
47
|
export const SPOKE_CHECKOUT_PAGE_FILES = Object.freeze([
|
|
45
48
|
"apps/web/app/[locale]/checkout/thank-you/page.tsx",
|
|
46
49
|
"apps/web/app/[locale]/checkout/unavailable/page.tsx",
|
|
47
50
|
]);
|
|
48
51
|
/**
|
|
49
|
-
* The
|
|
50
|
-
*
|
|
51
|
-
* the
|
|
52
|
-
*
|
|
52
|
+
* The spoke checkout's template paths, scaffold-relative (#8748 D3). A
|
|
53
|
+
* directory entry covers every file under it, so the tests that sit beside
|
|
54
|
+
* the pages travel with them. `copyTemplate` skips all of them for every
|
|
55
|
+
* scaffold; `writeSpokeCheckoutSlice` copies them into a commerce spoke.
|
|
53
56
|
*/
|
|
54
57
|
export const SPOKE_CHECKOUT_TEMPLATE_PATHS = Object.freeze([
|
|
55
|
-
"apps/web/app/api/checkout",
|
|
56
|
-
"apps/web/app/api/webhooks/stripe",
|
|
57
58
|
"apps/web/app/[locale]/checkout",
|
|
58
|
-
"apps/web/lib/checkout.ts",
|
|
59
59
|
SPOKE_CHECKOUT_CONFIG_PATH,
|
|
60
|
-
"apps/web/lib/__tests__/checkout.test.ts",
|
|
61
60
|
]);
|
|
62
|
-
/** The variables a commerce spoke depends on (D9)
|
|
61
|
+
/** The variables a commerce spoke depends on (D9): the package reads both. */
|
|
63
62
|
export const SPOKE_CHECKOUT_ENV_CATALOG = Object.freeze({
|
|
64
63
|
STRIPE_SECRET_KEY: {
|
|
65
|
-
description: `Read by ${SPOKE_CHECKOUT_PACKAGE}: a test key (sk_test_ or rk_test_), or a restricted live key the operator sets with STRIPE_MODE=live.`,
|
|
66
|
-
secret: true,
|
|
67
|
-
},
|
|
68
|
-
STRIPE_WEBHOOK_SECRET: {
|
|
69
|
-
description: `Read by ${SPOKE_CHECKOUT_PACKAGE}: the signing secret of this site's Stripe webhook endpoint.`,
|
|
64
|
+
description: `Read by ${SPOKE_CHECKOUT_PACKAGE} for the thank-you page's paid-session read and the billing reads, never to sell: a test key (sk_test_ or rk_test_), or a restricted live key the operator sets with STRIPE_MODE=live.`,
|
|
70
65
|
secret: true,
|
|
71
66
|
},
|
|
72
67
|
STRIPE_MODE: {
|
|
73
68
|
description: `Read by ${SPOKE_CHECKOUT_PACKAGE}: test (the default) or live. Live is the operator's switch, per site.`,
|
|
74
69
|
secret: false,
|
|
75
70
|
},
|
|
76
|
-
// Read by the standard spoke checkout's fulfilment, so the base catalog
|
|
77
|
-
// carries it too (#8748).
|
|
78
|
-
AILK_SPOKE_KEY: ENV_CATALOG["AILK_SPOKE_KEY"],
|
|
79
71
|
});
|
|
80
72
|
/** The names a commerce spoke declares although only the package reads them (D9). */
|
|
81
73
|
export const SPOKE_CHECKOUT_DECLARED_ENV = Object.freeze([
|
|
82
74
|
"STRIPE_SECRET_KEY",
|
|
83
|
-
"STRIPE_WEBHOOK_SECRET",
|
|
84
75
|
"STRIPE_MODE",
|
|
85
76
|
]);
|
|
86
77
|
/**
|
|
87
78
|
* The catalog (and declared names) a spoke's `appendEnvRows` call passes: a
|
|
88
|
-
* commerce spoke adds the
|
|
79
|
+
* commerce spoke adds the package's two rows (D9). `ENV_CATALOG` itself is
|
|
89
80
|
* unchanged — its test fails on an entry the monorepo's apps never read.
|
|
90
81
|
*/
|
|
91
82
|
export function spokeEnvRowOptions(commerce) {
|
|
@@ -96,7 +87,7 @@ export function spokeEnvRowOptions(commerce) {
|
|
|
96
87
|
}
|
|
97
88
|
: { catalog: ENV_CATALOG };
|
|
98
89
|
}
|
|
99
|
-
/** `metadata.site`
|
|
90
|
+
/** `metadata.site` — the package's own pattern. */
|
|
100
91
|
const SITE_PATTERN = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
101
92
|
/** A stamp a renderer may interpolate: the package's pattern, never `default` (D6). */
|
|
102
93
|
function isValidSite(site) {
|
|
@@ -125,14 +116,17 @@ export function spokeCheckoutSite(projectName) {
|
|
|
125
116
|
const digest = createHash("sha256").update(projectName).digest("hex");
|
|
126
117
|
return `site-${digest.slice(0, 8)}`;
|
|
127
118
|
}
|
|
119
|
+
/** The billing action's first line, which marks the file as create-ailk's to rewrite. */
|
|
120
|
+
export const BILLING_ACTION_MARKER = "// The spoke billing action — GENERATED by create-ailk (#8217). Do not edit:";
|
|
128
121
|
/**
|
|
129
122
|
* The spoke billing action (#8217 D6): wiring only, byte-identical across
|
|
130
123
|
* sites, so every guarantee arrives by a package bump. A plain server module
|
|
131
124
|
* (C2): its two exports take no argument, and the hub's read is its only
|
|
132
|
-
* source of customer ids.
|
|
125
|
+
* source of customer ids. It builds its config from the site's stamp and the
|
|
126
|
+
* site's own origin (#9142: no longer from `lib/checkout.ts`).
|
|
133
127
|
*/
|
|
134
|
-
const BILLING_ACTION = [
|
|
135
|
-
|
|
128
|
+
export const BILLING_ACTION = [
|
|
129
|
+
BILLING_ACTION_MARKER,
|
|
136
130
|
"// every guarantee lives in the spoke-checkout package and arrives by a",
|
|
137
131
|
"// package bump. A plain server module, not a Server Action file: neither",
|
|
138
132
|
"// export takes an argument, and the hub's GET /v1/member/billing is the only",
|
|
@@ -141,206 +135,29 @@ const BILLING_ACTION = [
|
|
|
141
135
|
`import { member } from "${API_CLIENT_PACKAGE}";`,
|
|
142
136
|
`import { createSpokeCheckout } from "${SPOKE_CHECKOUT_PACKAGE}";`,
|
|
143
137
|
"",
|
|
144
|
-
`import {
|
|
138
|
+
`import { checkoutConfig } from "../../../../../lib/checkout-config";`,
|
|
145
139
|
`import { forwardedCookieInit } from "../../../../../lib/server-api";`,
|
|
140
|
+
`import { resolveBaseUrl } from "../../../../../src/lib/routes";`,
|
|
146
141
|
"",
|
|
147
|
-
"const billing = createSpokeCheckout(
|
|
148
|
-
"
|
|
149
|
-
")
|
|
142
|
+
"const billing = createSpokeCheckout({",
|
|
143
|
+
" site: checkoutConfig.site,",
|
|
144
|
+
" siteBaseUrl: resolveBaseUrl(),",
|
|
145
|
+
"}).billing.forMember(async () => member.billing(await forwardedCookieInit()));",
|
|
150
146
|
"",
|
|
151
147
|
"export const readSpokeBilling = billing.read;",
|
|
152
148
|
"export const spokeBillingPortalUrl = billing.portalUrl;",
|
|
153
149
|
"",
|
|
154
150
|
].join("\n");
|
|
155
|
-
// ── #7839 D8 — the checkout e2e workflow ─────────────────────────────────────
|
|
156
|
-
/** Where a commerce spoke's end-to-end checkout workflow lives. */
|
|
157
|
-
export const SPOKE_CHECKOUT_E2E_WORKFLOW_PATH = ".github/workflows/checkout-e2e.yml";
|
|
158
|
-
/**
|
|
159
|
-
* The Stripe CLI release the workflow installs, and the SHA-256 of its
|
|
160
|
-
* `stripe_<v>_linux_x86_64.tar.gz` from that release's own
|
|
161
|
-
* `stripe-linux-checksums.txt`. Never bump one without re-reading the other.
|
|
162
|
-
*/
|
|
163
|
-
export const STRIPE_CLI_VERSION = "1.51.1";
|
|
164
|
-
export const STRIPE_CLI_LINUX_X64_SHA256 = "38c8de73a8cab810a4aab8a4015cd4e3398b7b87aa42b6944d09d89ed524ac53";
|
|
165
|
-
/** The Playwright the workflow installs — the monorepo's own pinned version. */
|
|
166
|
-
export const E2E_PLAYWRIGHT_VERSION = "1.61.1";
|
|
167
|
-
/**
|
|
168
|
-
* Every action the workflow uses, pinned to the commit its `v4` tag named
|
|
169
|
-
* when this was written (SEC-7839-2): `gh api repos/<action>/commits/v4`.
|
|
170
|
-
*/
|
|
171
|
-
export const E2E_ACTION_PINS = Object.freeze({
|
|
172
|
-
"actions/checkout": "11d5960a326750d5838078e36cf38b85af677262",
|
|
173
|
-
"pnpm/action-setup": "b906affcce14559ad1aafd4ab0e942779e9f58b1",
|
|
174
|
-
"actions/setup-node": "49933ea5288caeca8642d1e84afbd3f7d6820020",
|
|
175
|
-
"actions/upload-artifact": "ea165f8d65b6e75b540449e92b4886f43607fa02",
|
|
176
|
-
});
|
|
177
|
-
function uses(action) {
|
|
178
|
-
return `uses: ${action}@${E2E_ACTION_PINS[action]} # v4`;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* The workflow text (D8). Identical for every spoke at one create-ailk
|
|
182
|
-
* version. Every `${{ … }}` sits in an env-style value, never in a script.
|
|
183
|
-
*/
|
|
184
|
-
export const CHECKOUT_E2E_WORKFLOW = `# Checkout e2e — GENERATED by create-ailk (#7839). Do not edit: it changes
|
|
185
|
-
# with the ${SPOKE_CHECKOUT_PACKAGE} command it runs.
|
|
186
|
-
#
|
|
187
|
-
# Pays each named offering once through Stripe's hosted Checkout in TEST
|
|
188
|
-
# mode, then checks Stripe's webhook was answered 2xx and this site's
|
|
189
|
-
# fulfilment ran. Start it by hand (Actions > Checkout e2e > Run workflow)
|
|
190
|
-
# after a checkout change and before any live flip. It never runs on a push
|
|
191
|
-
# or a pull request: it needs the network, the Stripe CLI and test-mode state.
|
|
192
|
-
#
|
|
193
|
-
# Setup, once: create the GitHub Environment "checkout-e2e", limit its
|
|
194
|
-
# deployment branches to the default branch, and add to it the secrets
|
|
195
|
-
# STRIPE_TEST_SECRET_KEY (an sk_test_ or rk_test_ key) and AILK_SPOKE_KEY,
|
|
196
|
-
# and the variable AILK_API_URL (https). Each paid session posts one
|
|
197
|
-
# test-mode lead to the hub. Test subscriptions it creates are cancelled.
|
|
198
|
-
name: Checkout e2e
|
|
199
|
-
|
|
200
|
-
on:
|
|
201
|
-
workflow_dispatch:
|
|
202
|
-
inputs:
|
|
203
|
-
offerings:
|
|
204
|
-
description: Comma-separated fixed offering slugs from apps/web/lib/checkout-config.ts
|
|
205
|
-
required: true
|
|
206
|
-
type: string
|
|
207
|
-
|
|
208
|
-
permissions:
|
|
209
|
-
contents: read
|
|
210
|
-
|
|
211
|
-
concurrency:
|
|
212
|
-
group: checkout-e2e
|
|
213
|
-
cancel-in-progress: false
|
|
214
|
-
|
|
215
|
-
jobs:
|
|
216
|
-
checkout-e2e:
|
|
217
|
-
runs-on: ubuntu-latest
|
|
218
|
-
timeout-minutes: 30
|
|
219
|
-
environment: checkout-e2e
|
|
220
|
-
env:
|
|
221
|
-
NEXT_PUBLIC_BASE_URL: http://localhost:3000
|
|
222
|
-
STRIPE_MODE: test
|
|
223
|
-
steps:
|
|
224
|
-
- name: Refuse a live key or a missing setting
|
|
225
|
-
env:
|
|
226
|
-
STRIPE_SECRET_KEY: \${{ secrets.STRIPE_TEST_SECRET_KEY }}
|
|
227
|
-
AILK_SPOKE_KEY: \${{ secrets.AILK_SPOKE_KEY }}
|
|
228
|
-
AILK_API_URL: \${{ vars.AILK_API_URL }}
|
|
229
|
-
run: |
|
|
230
|
-
case "$STRIPE_SECRET_KEY" in
|
|
231
|
-
sk_test_*|rk_test_*) ;;
|
|
232
|
-
*) echo "::error::STRIPE_TEST_SECRET_KEY must be an sk_test_ or rk_test_ key"; exit 2 ;;
|
|
233
|
-
esac
|
|
234
|
-
if [ -z "$AILK_SPOKE_KEY" ]; then
|
|
235
|
-
echo "::error::set the AILK_SPOKE_KEY secret in the checkout-e2e environment"; exit 2
|
|
236
|
-
fi
|
|
237
|
-
case "$AILK_API_URL" in
|
|
238
|
-
https://*) ;;
|
|
239
|
-
*) echo "::error::AILK_API_URL must be the hub's https origin"; exit 2 ;;
|
|
240
|
-
esac
|
|
241
|
-
|
|
242
|
-
- ${uses("actions/checkout")}
|
|
243
|
-
|
|
244
|
-
- ${uses("pnpm/action-setup")}
|
|
245
|
-
|
|
246
|
-
- run: test -f pnpm-lock.yaml || touch pnpm-lock.yaml
|
|
247
|
-
|
|
248
|
-
- ${uses("actions/setup-node")}
|
|
249
|
-
with:
|
|
250
|
-
node-version: 20
|
|
251
|
-
cache: pnpm
|
|
252
|
-
cache-dependency-path: pnpm-lock.yaml
|
|
253
|
-
|
|
254
|
-
- name: Install
|
|
255
|
-
run: |
|
|
256
|
-
if [ -s pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; else pnpm install; fi
|
|
257
|
-
|
|
258
|
-
- name: Install the Stripe CLI (pinned, checksum-checked)
|
|
259
|
-
run: |
|
|
260
|
-
curl -fsSL -o "$RUNNER_TEMP/stripe.tar.gz" \\
|
|
261
|
-
https://github.com/stripe/stripe-cli/releases/download/v${STRIPE_CLI_VERSION}/stripe_${STRIPE_CLI_VERSION}_linux_x86_64.tar.gz
|
|
262
|
-
echo "${STRIPE_CLI_LINUX_X64_SHA256} $RUNNER_TEMP/stripe.tar.gz" | sha256sum -c -
|
|
263
|
-
mkdir -p "$RUNNER_TEMP/stripe-cli"
|
|
264
|
-
tar -xzf "$RUNNER_TEMP/stripe.tar.gz" -C "$RUNNER_TEMP/stripe-cli" stripe
|
|
265
|
-
|
|
266
|
-
- name: Install Playwright (pinned) and Chromium
|
|
267
|
-
run: |
|
|
268
|
-
npm install --no-save --ignore-scripts --prefix "$RUNNER_TEMP/e2e-deps" playwright@${E2E_PLAYWRIGHT_VERSION}
|
|
269
|
-
"$RUNNER_TEMP/e2e-deps/node_modules/.bin/playwright" install --with-deps chromium
|
|
270
|
-
|
|
271
|
-
- name: Fetch the Stripe CLI's signing secret
|
|
272
|
-
env:
|
|
273
|
-
STRIPE_API_KEY: \${{ secrets.STRIPE_TEST_SECRET_KEY }}
|
|
274
|
-
run: |
|
|
275
|
-
umask 077
|
|
276
|
-
secret="$("$RUNNER_TEMP/stripe-cli/stripe" listen --print-secret)"
|
|
277
|
-
echo "::add-mask::$secret"
|
|
278
|
-
printf '%s' "$secret" > "$RUNNER_TEMP/stripe-whsec"
|
|
279
|
-
|
|
280
|
-
- name: Build the site
|
|
281
|
-
run: pnpm --filter ./apps/web build
|
|
282
|
-
|
|
283
|
-
- name: Start the site
|
|
284
|
-
env:
|
|
285
|
-
STRIPE_SECRET_KEY: \${{ secrets.STRIPE_TEST_SECRET_KEY }}
|
|
286
|
-
AILK_SPOKE_KEY: \${{ secrets.AILK_SPOKE_KEY }}
|
|
287
|
-
AILK_API_URL: \${{ vars.AILK_API_URL }}
|
|
288
|
-
run: |
|
|
289
|
-
STRIPE_WEBHOOK_SECRET="$(cat "$RUNNER_TEMP/stripe-whsec")"
|
|
290
|
-
export STRIPE_WEBHOOK_SECRET
|
|
291
|
-
cd apps/web
|
|
292
|
-
nohup pnpm exec next start -p 3000 -H localhost > "$RUNNER_TEMP/next.log" 2>&1 &
|
|
293
|
-
echo $! > "$RUNNER_TEMP/next.pid"
|
|
294
|
-
for _ in $(seq 1 60); do
|
|
295
|
-
curl -fsS -o /dev/null http://localhost:3000/checkout/thank-you && exit 0
|
|
296
|
-
sleep 2
|
|
297
|
-
done
|
|
298
|
-
echo "::error::the site did not start on localhost:3000; run it locally with the same settings to see why"
|
|
299
|
-
exit 1
|
|
300
|
-
|
|
301
|
-
- name: Pay each offering
|
|
302
|
-
working-directory: apps/web
|
|
303
|
-
env:
|
|
304
|
-
STRIPE_SECRET_KEY: \${{ secrets.STRIPE_TEST_SECRET_KEY }}
|
|
305
|
-
OFFERINGS: \${{ inputs.offerings }}
|
|
306
|
-
SPOKE_CHECKOUT_E2E_DEPS: \${{ runner.temp }}/e2e-deps
|
|
307
|
-
STRIPE_CLI_PATH: \${{ runner.temp }}/stripe-cli/stripe
|
|
308
|
-
run: |
|
|
309
|
-
STRIPE_WEBHOOK_SECRET="$(cat "$RUNNER_TEMP/stripe-whsec")"
|
|
310
|
-
export STRIPE_WEBHOOK_SECRET
|
|
311
|
-
pnpm exec spoke-checkout-e2e --base-url http://localhost:3000 --offerings "$OFFERINGS" --out "$RUNNER_TEMP/checkout-e2e"
|
|
312
|
-
|
|
313
|
-
- name: Stop the site
|
|
314
|
-
if: always()
|
|
315
|
-
run: |
|
|
316
|
-
if [ -f "$RUNNER_TEMP/next.pid" ]; then pkill -P "$(cat "$RUNNER_TEMP/next.pid")" || true; kill "$(cat "$RUNNER_TEMP/next.pid")" || true; fi
|
|
317
|
-
rm -f "$RUNNER_TEMP/stripe-whsec"
|
|
318
|
-
|
|
319
|
-
- name: Keep the report
|
|
320
|
-
if: always()
|
|
321
|
-
${uses("actions/upload-artifact")}
|
|
322
|
-
with:
|
|
323
|
-
name: checkout-e2e
|
|
324
|
-
path: \${{ runner.temp }}/checkout-e2e
|
|
325
|
-
if-no-files-found: ignore
|
|
326
|
-
retention-days: 7
|
|
327
|
-
`;
|
|
328
151
|
/**
|
|
329
152
|
* The rendered part of the slice for `site` (#8748 D3): with `signIn: "hub"`
|
|
330
|
-
* the billing action (#8217 D8),
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
153
|
+
* the billing action (#8217 D8), else nothing. It does not depend on `site`.
|
|
154
|
+
* The rest of the slice is template source, copied by
|
|
155
|
+
* `writeSpokeCheckoutSlice`. Throws `TopologyError` for an invalid stamp
|
|
156
|
+
* (SEC-7838-10).
|
|
334
157
|
*/
|
|
335
158
|
export function renderSpokeCheckoutFiles(site, opts = {}) {
|
|
336
159
|
assertSite(site);
|
|
337
|
-
|
|
338
|
-
return [
|
|
339
|
-
...(opts.signIn === "hub"
|
|
340
|
-
? [[billingActionPath, BILLING_ACTION]]
|
|
341
|
-
: []),
|
|
342
|
-
[SPOKE_CHECKOUT_E2E_WORKFLOW_PATH, CHECKOUT_E2E_WORKFLOW],
|
|
343
|
-
];
|
|
160
|
+
return opts.signIn === "hub" ? [[SPOKE_BILLING_ACTION_PATH, BILLING_ACTION]] : [];
|
|
344
161
|
}
|
|
345
162
|
/** The one line of the template's config the writer stamps (#8748 D2). */
|
|
346
163
|
const SITE_LINE = /^const SITE = "[a-z0-9-]+";$/gm;
|
|
@@ -438,9 +255,10 @@ function isOlderPin(current, wanted) {
|
|
|
438
255
|
* action, and the web seam — generated, so always rewritten — becomes the
|
|
439
256
|
* billing render: reported `patched` when it changed, not at all when equal.
|
|
440
257
|
* Adds the package to `apps/web/package.json` when absent, and raises a plain
|
|
441
|
-
* pin older than the one it writes (#7839): the
|
|
442
|
-
*
|
|
443
|
-
* newer, `workspace:`, a tag —
|
|
258
|
+
* pin older than the one it writes (#7839): the rendered billing action
|
|
259
|
+
* calls the package's current `createSpokeCheckout` shape, which an older
|
|
260
|
+
* pin does not take (#9142). Any other range — newer, `workspace:`, a tag —
|
|
261
|
+
* is the site's and is kept.
|
|
444
262
|
* Every refusal (an invalid stamp, a version map without the package, a
|
|
445
263
|
* missing or malformed `apps/web/package.json`, a template missing any
|
|
446
264
|
* `SPOKE_CHECKOUT_TEMPLATE_PATHS` entry, a template config without exactly
|
|
@@ -558,3 +376,22 @@ export function dropSpokeCheckoutDependency(targetDir) {
|
|
|
558
376
|
delete deps[SPOKE_CHECKOUT_PACKAGE];
|
|
559
377
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}${before.endsWith("\n") ? "\n" : ""}`);
|
|
560
378
|
}
|
|
379
|
+
// ── #9142 — the retired legacy env-key checkout ─────────────────────────────
|
|
380
|
+
/**
|
|
381
|
+
* What the legacy env-key checkout left in a spoke, scaffold-relative: the
|
|
382
|
+
* buy route and the Stripe webhook route (a directory entry covers the tests
|
|
383
|
+
* beside each), `lib/checkout.ts` (the fulfilment) and its test. Each was a
|
|
384
|
+
* template copy create-ailk recorded in `.ailk-template-files.json`;
|
|
385
|
+
* `create-ailk sync-routes --apply` removes the ones still byte-identical to
|
|
386
|
+
* that record.
|
|
387
|
+
*/
|
|
388
|
+
export const RETIRED_SPOKE_CHECKOUT_PATHS = Object.freeze([
|
|
389
|
+
"apps/web/app/api/checkout",
|
|
390
|
+
"apps/web/app/api/webhooks/stripe",
|
|
391
|
+
"apps/web/lib/checkout.ts",
|
|
392
|
+
"apps/web/lib/__tests__/checkout.test.ts",
|
|
393
|
+
]);
|
|
394
|
+
/** The retired checkout e2e workflow — rendered, never recorded (#7839 D8). */
|
|
395
|
+
export const RETIRED_CHECKOUT_E2E_WORKFLOW_PATH = ".github/workflows/checkout-e2e.yml";
|
|
396
|
+
/** The workflow's first line, which marks it as create-ailk's to remove. */
|
|
397
|
+
export const RETIRED_CHECKOUT_E2E_MARKER = "# Checkout e2e — GENERATED by create-ailk (#7839). Do not edit: it changes";
|
package/dist/starter-grid.d.ts
CHANGED
|
@@ -109,9 +109,6 @@ export declare const ALIASES: Readonly<{
|
|
|
109
109
|
readonly design: "standard";
|
|
110
110
|
readonly modules: readonly ["marketing"];
|
|
111
111
|
readonly categories: readonly ["accounting", "legal", "technology"];
|
|
112
|
-
readonly layout: {
|
|
113
|
-
readonly announcementBanner: true;
|
|
114
|
-
};
|
|
115
112
|
};
|
|
116
113
|
/**
|
|
117
114
|
* The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
|
package/dist/starter-grid.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The pages every starter carries whatever its shape (#6658 D5,
|
|
3
3
|
* `docs/decisions/starter-roster-page-set.md` D4/D7): a glossary and a
|
|
4
|
-
* newsletter signup
|
|
5
|
-
* roster enables, with its seed in `_base
|
|
4
|
+
* newsletter signup — each a module-less `GATEABLE_SURFACES` entry the
|
|
5
|
+
* roster enables, with its seed in `_base` — and the `/unsubscribe` landing
|
|
6
|
+
* every signed link calls (#8850 D1). That one is a code route with no
|
|
7
|
+
* `content`, like `/sign-in`: a `CORE_ROUTE_DIRS` entry every scaffold
|
|
8
|
+
* already serves, never gated (#8438 D17), declared here so the roster of
|
|
9
|
+
* record lists it beside the newsletter.
|
|
6
10
|
*
|
|
7
|
-
*
|
|
8
|
-
* former `/book-a-call` link now points at `/meet/book-a-call`,
|
|
9
|
-
* `FREE_MEET_PLACEHOLDER_ROWS` below (a Free placeholder) or by
|
|
10
|
-
* scheduling module's own live `/meet/[slug]` page.
|
|
11
|
+
* A former third page — `/book-a-call` — was retired by #6897 D10: every
|
|
12
|
+
* scaffold's former `/book-a-call` link now points at `/meet/book-a-call`,
|
|
13
|
+
* served by `FREE_MEET_PLACEHOLDER_ROWS` below (a Free placeholder) or by
|
|
14
|
+
* the scheduling module's own live `/meet/[slug]` page.
|
|
11
15
|
*/
|
|
12
16
|
const SHAPE_INDEPENDENT_ROWS = [
|
|
13
17
|
{ route: "/glossary", content: ["content/en/GlossaryList/glossary.mdx"] },
|
|
@@ -15,6 +19,7 @@ const SHAPE_INDEPENDENT_ROWS = [
|
|
|
15
19
|
route: "/newsletter",
|
|
16
20
|
content: ["content/en/NewsletterSignup/newsletter.mdx"],
|
|
17
21
|
},
|
|
22
|
+
{ route: "/unsubscribe" },
|
|
18
23
|
];
|
|
19
24
|
/**
|
|
20
25
|
* The Free `/meet` placeholder pages (#6897 D5) — a meeting-types hub
|
|
@@ -30,10 +35,12 @@ const SHAPE_INDEPENDENT_ROWS = [
|
|
|
30
35
|
* Every alias that takes the `scheduling` module drops `"/meet"` and
|
|
31
36
|
* `"/meet/[slug]"` (D5) — otherwise `mergeRoster` refuses the duplicate
|
|
32
37
|
* route the module's own rows also contribute. The ROWS go; the SEEDS stay
|
|
33
|
-
* (#6942, operator 2026-09-28, partly reversing D5):
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* (`site-roster.ts`) folds them onto the module's rows.
|
|
38
|
+
* (#6942, operator 2026-09-28, partly reversing D5): the module's meeting
|
|
39
|
+
* page reads a `BookCall/<slug>` seed for its URL, its JSON-LD and its
|
|
40
|
+
* fallback title, never its sections (#8843 C5). `withMeetPlaceholderSeeds`
|
|
41
|
+
* (`site-roster.ts`) folds them onto the module's rows. The hub-driven
|
|
42
|
+
* `/private` index has no row here: it reaches those aliases through the
|
|
43
|
+
* module's own `pageContributions` (#8843 D8, #8850 D2).
|
|
37
44
|
*/
|
|
38
45
|
const FREE_MEET_PLACEHOLDER_ROWS = [
|
|
39
46
|
{ route: "/meet", content: ["content/en/LandingPage/meet.mdx"] },
|
|
@@ -391,9 +398,12 @@ export const ALIASES = Object.freeze({
|
|
|
391
398
|
design: "standard",
|
|
392
399
|
modules: ["marketing"],
|
|
393
400
|
categories: ["accounting", "legal", "technology"],
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
|
|
401
|
+
// No `layout`: every knob keeps its default. The "Get started for free or
|
|
402
|
+
// upgrade to Pro" banner this alias carried for the demo site (operator
|
|
403
|
+
// ruling 2026-09-23) reached every keyless `organization-product`
|
|
404
|
+
// scaffold too, so Epic #8845's "no starter page ships our own brands or
|
|
405
|
+
// upsells" retires it (#8850, spec C6); a demo deployment that wants it
|
|
406
|
+
// sets it in its own config.
|
|
397
407
|
},
|
|
398
408
|
/**
|
|
399
409
|
* The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
|