create-ailk 0.21.0 → 0.22.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 +37 -3
- package/component-catalog.md +6 -5
- package/dist/add-module.js +9 -5
- package/dist/derive-architecture.d.ts +8 -1
- package/dist/derive-architecture.js +38 -3
- package/dist/design-package.js +7 -2
- package/dist/lib/env-catalog.js +5 -1
- package/dist/module-architecture.js +4 -2
- package/dist/programmatic.js +19 -5
- package/dist/site-chrome.d.ts +8 -0
- package/dist/site-chrome.js +17 -1
- package/dist/spoke-checkout.d.ts +39 -18
- package/dist/spoke-checkout.js +177 -236
- package/dist/surfaces.js +19 -14
- package/dist/sync-routes.js +3 -2
- package/dist/template-record.d.ts +4 -3
- package/dist/template-record.js +6 -3
- package/dist/topology.js +25 -1
- package/dist/update.js +5 -0
- package/package.json +3 -3
- package/site-templates/designs/minimalism/DESIGN.md +1 -1
- package/templates/apps/api/CLAUDE.md +15 -6
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +19 -0
- package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +149 -0
- package/templates/apps/api/src/lib/consent-store.ts +28 -0
- package/templates/apps/api/src/lib/flow-completion-store.ts +136 -0
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +38 -2
- package/templates/apps/api/src/lib/proposal-paid-store.ts +21 -4
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +14 -3
- package/templates/apps/api/src/middleware/auth.ts +13 -6
- package/templates/apps/api/src/openapi/spec.ts +30 -3
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +49 -0
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +41 -0
- package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +386 -0
- package/templates/apps/api/src/routes/consent/account.ts +266 -0
- package/templates/apps/api/src/routes/consent/index.ts +15 -7
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +36 -0
- package/templates/apps/api/src/routes/content/get-page.ts +4 -0
- package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +515 -0
- package/templates/apps/api/src/routes/leads/index.ts +161 -7
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +649 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +154 -19
- package/templates/apps/api/src/server.ts +28 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +1038 -118
- package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +389 -0
- package/templates/apps/api/src/services/checkout-buyer.ts +100 -4
- package/templates/apps/api/src/services/consent.ts +95 -0
- package/templates/apps/api/src/services/lead-routing.ts +337 -8
- package/templates/apps/api/src/services/magnet-completion.ts +242 -0
- package/templates/apps/api/src/services/person.ts +40 -8
- package/templates/apps/api/src/services/plan-offerings.ts +59 -0
- package/templates/apps/api/src/services/public-site-resolver.ts +8 -1
- package/templates/apps/web/.env.example +14 -0
- package/templates/apps/web/CLAUDE.md +120 -9
- package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +6 -2
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +5 -4
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +5 -1
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +2 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +3 -14
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +2 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +5 -1
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +160 -8
- package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/sign-in-card.test.tsx +12 -9
- package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +105 -2
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +36 -3
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +47 -1
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +60 -22
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +16 -8
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +85 -31
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +18 -4
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +3 -1
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +7 -4
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/agreements-prompt.test.tsx +227 -207
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +17 -1
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +143 -1
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +4 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +385 -89
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +5 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +44 -5
- package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +68 -34
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +0 -8
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-actions.ts +39 -151
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-gate.ts +61 -0
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-prompt.tsx +143 -140
- package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +40 -6
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +68 -54
- package/templates/apps/web/app/[locale]/(authed)/account/privacy/page.tsx +5 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/apply.actions.ts +293 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/job-application-form.ts +115 -0
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +146 -1
- package/templates/apps/web/app/[locale]/careers/__tests__/apply.actions.test.ts +310 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-application-form.test.ts +143 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/job-apply-page.test.tsx +350 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +29 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/checkout/__tests__/pages.test.tsx +139 -0
- package/templates/apps/web/app/[locale]/checkout/__tests__/thank-you-account.test.tsx +136 -0
- package/templates/apps/web/app/[locale]/checkout/thank-you/page.tsx +114 -0
- package/templates/apps/web/app/[locale]/checkout/unavailable/page.tsx +51 -0
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +17 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +79 -4
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-magnet-page.test.tsx +41 -2
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +438 -31
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +7 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +168 -57
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +26 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +49 -17
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +106 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.invite.test.tsx +257 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/account-invite.actions.test.ts +69 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/account-invite.actions.ts +35 -0
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/AccountInviteLink.tsx +89 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/AccountInviteLink.test.tsx +104 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +38 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +114 -0
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +20 -3
- package/templates/apps/web/app/api/checkout/[offering]/route.ts +48 -0
- package/templates/apps/web/app/api/checkout/__tests__/route.test.ts +120 -0
- package/templates/apps/web/app/api/checkout/__tests__/success-path.test.ts +86 -0
- package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +44 -1
- package/templates/apps/web/app/api/hub-session/sign-in/route.ts +22 -3
- package/templates/apps/web/app/api/webhooks/stripe/__tests__/route.test.ts +92 -0
- package/templates/apps/web/app/api/webhooks/stripe/route.ts +31 -0
- package/templates/apps/web/auth.ts +28 -2
- package/templates/apps/web/lib/__tests__/account-invite.test.ts +63 -0
- package/templates/apps/web/lib/__tests__/checkout.test.ts +243 -0
- package/templates/apps/web/lib/__tests__/consent-policies.test.ts +23 -0
- package/templates/apps/web/lib/__tests__/deck-page.test.ts +5 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +34 -17
- package/templates/apps/web/lib/account-invite.ts +37 -0
- package/templates/apps/web/lib/checkout-config.ts +27 -0
- package/templates/apps/web/lib/checkout.ts +93 -0
- package/templates/apps/web/lib/consent-policies.ts +22 -0
- package/templates/apps/web/lib/deck-page.ts +6 -0
- package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +111 -0
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +88 -32
- package/templates/apps/web/lib/prisma-for-auth.ts +8 -0
- package/templates/apps/web/lib/sign-up-agreements.ts +447 -0
- package/templates/apps/web/lib/site-chrome.ts +2 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
- package/templates/apps/web/messages/en.json +24 -7
- package/templates/apps/web/package.json +1 -0
- package/templates/apps/web/src/lib/__tests__/lead-write.test.ts +187 -0
- package/templates/apps/web/src/lib/__tests__/rate-limit.test.ts +67 -0
- package/templates/apps/web/src/lib/lead-write.ts +90 -0
- package/templates/apps/web/src/lib/rate-limit.ts +79 -0
- package/templates/database/CHANGELOG.md +38 -0
- package/templates/database/__tests__/consent-source-checkout-migration.test.ts +79 -0
- package/templates/database/__tests__/flow-completion-migration.test.ts +115 -0
- package/templates/database/__tests__/flow-completion.db.test.ts +136 -0
- package/templates/database/__tests__/seed.test.ts +34 -1
- package/templates/database/inbox/schema.prisma +17 -2
- package/templates/database/migrations/20261018120000_flow_completion/migration.sql +52 -0
- package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +27 -0
- package/templates/database/package.json +1 -1
- package/templates/database/seed.ts +34 -3
- package/versions.json +34 -34
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up.actions.ts +0 -125
package/component-catalog.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"importPath": "@working-theory/ui/blocks",
|
|
18
18
|
"rootBarrel": "@working-theory/ui",
|
|
19
19
|
"sourceRoot": "packages/ui/src/blocks",
|
|
20
|
-
"count":
|
|
20
|
+
"count": 107
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "chrome",
|
|
@@ -837,6 +837,17 @@
|
|
|
837
837
|
"descriptionGrade": "strong",
|
|
838
838
|
"sectionType": null
|
|
839
839
|
},
|
|
840
|
+
{
|
|
841
|
+
"name": "AuthBackLink",
|
|
842
|
+
"tier": "blocks",
|
|
843
|
+
"sourcePath": "packages/ui/src/blocks/AuthBackLink.tsx",
|
|
844
|
+
"importPath": "@working-theory/ui/blocks",
|
|
845
|
+
"exported": true,
|
|
846
|
+
"description": "The auth shell's top-start \"← Back to <site>\". A hub's sign-in page renders it when the visitor arrived from a registered spoke, so the cross-domain handoff names where they came from and gives them the way back.",
|
|
847
|
+
"descriptionSource": "doc-comment",
|
|
848
|
+
"descriptionGrade": "strong",
|
|
849
|
+
"sectionType": null
|
|
850
|
+
},
|
|
840
851
|
{
|
|
841
852
|
"name": "AuthCard",
|
|
842
853
|
"tier": "blocks",
|
|
@@ -5393,6 +5404,9 @@
|
|
|
5393
5404
|
"headline": {
|
|
5394
5405
|
"type": "string"
|
|
5395
5406
|
},
|
|
5407
|
+
"subtitle": {
|
|
5408
|
+
"type": "string"
|
|
5409
|
+
},
|
|
5396
5410
|
"body": {
|
|
5397
5411
|
"type": "string"
|
|
5398
5412
|
},
|
|
@@ -12177,7 +12191,7 @@
|
|
|
12177
12191
|
"groups": [
|
|
12178
12192
|
{
|
|
12179
12193
|
"name": "colors",
|
|
12180
|
-
"leafCount":
|
|
12194
|
+
"leafCount": 64,
|
|
12181
12195
|
"leaves": [
|
|
12182
12196
|
{
|
|
12183
12197
|
"path": "colors.light.bg",
|
|
@@ -12199,6 +12213,11 @@
|
|
|
12199
12213
|
"type": "string",
|
|
12200
12214
|
"value": "#475569"
|
|
12201
12215
|
},
|
|
12216
|
+
{
|
|
12217
|
+
"path": "colors.light.link",
|
|
12218
|
+
"type": "string",
|
|
12219
|
+
"value": "#2563eb"
|
|
12220
|
+
},
|
|
12202
12221
|
{
|
|
12203
12222
|
"path": "colors.light.textMuted",
|
|
12204
12223
|
"type": "string",
|
|
@@ -12354,6 +12373,11 @@
|
|
|
12354
12373
|
"type": "string",
|
|
12355
12374
|
"value": "#94a3b8"
|
|
12356
12375
|
},
|
|
12376
|
+
{
|
|
12377
|
+
"path": "colors.dark.link",
|
|
12378
|
+
"type": "string",
|
|
12379
|
+
"value": "#60a5fa"
|
|
12380
|
+
},
|
|
12357
12381
|
{
|
|
12358
12382
|
"path": "colors.dark.textMuted",
|
|
12359
12383
|
"type": "string",
|
|
@@ -13187,7 +13211,7 @@
|
|
|
13187
13211
|
},
|
|
13188
13212
|
{
|
|
13189
13213
|
"name": "spacing",
|
|
13190
|
-
"leafCount":
|
|
13214
|
+
"leafCount": 15,
|
|
13191
13215
|
"leaves": [
|
|
13192
13216
|
{
|
|
13193
13217
|
"path": "spacing.controlX",
|
|
@@ -13234,6 +13258,16 @@
|
|
|
13234
13258
|
"type": "string",
|
|
13235
13259
|
"value": "1rem"
|
|
13236
13260
|
},
|
|
13261
|
+
{
|
|
13262
|
+
"path": "spacing.deckLabelLine",
|
|
13263
|
+
"type": "string",
|
|
13264
|
+
"value": "var(--spacing-section-lg)"
|
|
13265
|
+
},
|
|
13266
|
+
{
|
|
13267
|
+
"path": "spacing.deckActionLine",
|
|
13268
|
+
"type": "string",
|
|
13269
|
+
"value": "calc(2 * (var(--control-height-big) + var(--spacing-block-tight)))"
|
|
13270
|
+
},
|
|
13237
13271
|
{
|
|
13238
13272
|
"path": "spacing.paragraph",
|
|
13239
13273
|
"type": "string",
|
package/component-catalog.md
CHANGED
|
@@ -15,13 +15,13 @@ The component inventory of `@working-theory/ui` and the shells of `@working-theo
|
|
|
15
15
|
3. Check the Primitives, Panels and Shells tables too — a low-level control, a collapsible layout container, or a route-level frame lives there, not under Sections or Blocks.
|
|
16
16
|
4. Only then build. If you do build, you are declaring the capability is genuinely absent.
|
|
17
17
|
|
|
18
|
-
Inventory: **
|
|
18
|
+
Inventory: **320 component files** across 16 tiers — 56 sections, 107 blocks, 19 chrome, 79 primitives, 6 panels, 8 shells, 5 docs-blog-blocks, 1 docs-blog-chrome, 2 docs-blog-shells, 2 scheduling-sections, 6 scheduling-blocks, 5 signal-collectors-sections, 9 signal-collectors-blocks, 2 commerce-sections, 12 commerce-blocks, 1 commerce-panels. 317 distinct names: `SchedulerEmbed`, `Separator`, `Tabs` exist in more than one tier as genuinely different components, listed once per tier with its own source path.
|
|
19
19
|
|
|
20
|
-
Description coverage: **
|
|
20
|
+
Description coverage: **316/320** components carry a capability description derived from source — 44 from the section registry's hand-authored when-to-use text, 237 from the component's own doc-comment header, 35 from its tier barrel's roster line. **4** have none; they are listed under [Undescribed components](#undescribed-components) rather than given a sentence invented from their name.
|
|
21
21
|
|
|
22
|
-
Of those, **
|
|
22
|
+
Of those, **282** are *strong* — a capability sentence you could match a requirement against — and **34** are *weak*: either one short clause, or a long one that spends itself on wiring (client boundaries, class names, which primitives it composes) instead of on what the thing is for. Weak rows are marked `(weak)` and listed in full under [Weak descriptions](#weak-descriptions). Coverage is 99%, but **findable-without-luck coverage is 88%**.
|
|
23
23
|
|
|
24
|
-
Per tier, strong out of total: **Sections** 55/56 · **Blocks**
|
|
24
|
+
Per tier, strong out of total: **Sections** 55/56 · **Blocks** 99/107 · **Chrome** 10/19 · **Primitives** 64/79 (4 undescribed) · **Panels** 5/6 · **Shells** 7/8 · **Docs + Blog Blocks** 5/5 · **Docs + Blog Chrome** 1/1 · **Docs + Blog Shells** 0/2 · **Scheduling Sections** 2/2 · **Scheduling Blocks** 6/6 · **Signal-Collectors Sections** 5/5 · **Signal-Collectors Blocks** 8/9 · **Commerce Sections** 2/2 · **Commerce Blocks** 12/12 · **Commerce Panels** 1/1. A tier low on this line is a tier where a capability search is most likely to come up empty on something that exists.
|
|
25
25
|
|
|
26
26
|
That grade is **mechanical**. It counts what it can measure — how much a description adds beyond the component's own name, how much of it is spent on wiring, whether the extraction came out damaged — and it cannot tell a fluent paragraph about the wrong subject from a useful one. A person reading these tables will find more rows unusable than the weak count admits. Treat it as a floor on the problem, not a measurement of it, and read the source before concluding a capability is absent.
|
|
27
27
|
|
|
@@ -120,7 +120,7 @@ Import: `import { X } from "@working-theory/ui/sections";` (or from the `@workin
|
|
|
120
120
|
|
|
121
121
|
---
|
|
122
122
|
|
|
123
|
-
## Blocks (
|
|
123
|
+
## Blocks (107)
|
|
124
124
|
|
|
125
125
|
Intermediate composition tiles — the pieces sections are built from. Compose primitives and panels; never import a section.
|
|
126
126
|
|
|
@@ -136,6 +136,7 @@ Import: `import { X } from "@working-theory/ui/blocks";` (or from the `@working-
|
|
|
136
136
|
| `AskAiCommandGroup` | The ⌘K palette's composable "Ask AI" group; one CommandItem per provider seeding "About <entity>: <query>" (or the entity promptText when the query is empty); composed by the consumer's own CommandDialog. | `import { AskAiCommandGroup } from "@working-theory/ui/blocks"` |
|
|
137
137
|
| `AskAiProviderRow` | The ONE shared provider-link row every ask-AI placement composes — the AskAi section (band + ticker variant) and the Hero `askAi` opt-in all render THIS block, never a per-placement fork. Renders a bounded set of five measured providers, each a deep link seeded with an AUTHORED `promptText` — the prompt is always consumer-supplied data, never derived from page context (headline, entity name, etc.). | `import { AskAiProviderRow } from "@working-theory/ui/blocks"` |
|
|
138
138
|
| `AssetLightbox` | The asset detail lightbox: the image with zoom on the stage, a Details rail with in-place pencil-edit Title and Description, then Filename/Type/Size/Dimensions/Created, then Tags (from the seeded set the caller passes), then Copy link/Replace/Delete. Renders inside a ResponsiveSheet — the SAME field sequence in both layouts (: one field array, walked once, shared by both). | `import { AssetLightbox } from "@working-theory/ui/blocks"` |
|
|
139
|
+
| `AuthBackLink` | The auth shell's top-start "← Back to <site>". A hub's sign-in page renders it when the visitor arrived from a registered spoke, so the cross-domain handoff names where they came from and gives them the way back. | `import { AuthBackLink } from "@working-theory/ui/blocks"` |
|
|
139
140
|
| `AuthCard` | Multi-flow auth card: sign-in, sign-up, invitation, forgot, reset and sent, from one skeleton. One self-contained card renders every auth surface a site needs: signing in, signing up, a closed sign-up that requires an invitation, requesting a password reset, choosing a new password, and the check-your-email state that follows a magic link or a reset request. | `import { AuthCard } from "@working-theory/ui/blocks"` |
|
|
140
141
|
| `AwardBadgePlaceholder` | The labeled placeholder an Awards badge slot renders when no image has been supplied — mirrors MediaPlaceholder's role but in an award-chevron/ laurel badge silhouette rather than a generic photo frame, since an unfilled award slot should read as "an award badge goes here," not "a photo goes here." The shape (a laurel-flanked medallion over a chevron ribbon) is a hand-drawn SVG outline — never a copied G2/Capterra/Apollo asset. | internal — compose from `packages/ui/src/blocks/AwardBadgePlaceholder.tsx` |
|
|
141
142
|
| `BackgroundPattern` | Decorative SVG dot-grid / line pattern for ContactForm section. Decorative block (generalized beyond its original single consumer). Renders an absolutely-positioned decoration behind a section's content. Originally the pattern anchored behind ContactForm's form column; SpotlightPanel composes it as a full-bleed section backdrop (dimmed + blurred, behind a centered card) — same block, a second placement. | `import { BackgroundPattern } from "@working-theory/ui/blocks"` |
|
package/dist/add-module.js
CHANGED
|
@@ -168,16 +168,20 @@ export async function applyModuleToScaffold(opts) {
|
|
|
168
168
|
}
|
|
169
169
|
: {}),
|
|
170
170
|
});
|
|
171
|
-
// #7838 D8 — commerce on a spoke brings the spoke's own checkout
|
|
172
|
-
// from the
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
171
|
+
// #7838 D8 — commerce on a spoke brings the spoke's own checkout — the
|
|
172
|
+
// standard, copied from the template (#8748 D3) — stamped from the site
|
|
173
|
+
// directory's name (the `projectName` scaffold() defaults to). A file the
|
|
174
|
+
// site already has is left as it is. No record row: the record is written
|
|
175
|
+
// only at scaffold time. #8217 D8 — on a spoke that signs in through its
|
|
176
|
+
// hub, the billing action and its seam render too, so re-running
|
|
177
|
+
// add-module commerce gives an existing commerce spoke the action.
|
|
176
178
|
const spokeCommerce = spoke !== undefined && opts.moduleName === "commerce";
|
|
177
179
|
const checkoutRows = spokeCommerce
|
|
178
180
|
? writeSpokeCheckoutSlice(targetAbs, {
|
|
179
181
|
site: spokeCheckoutSite(basename(targetAbs)),
|
|
182
|
+
projectName: basename(targetAbs),
|
|
180
183
|
versions: opts.versions,
|
|
184
|
+
templatesDir: opts.templatesDir,
|
|
181
185
|
signIn: spoke.signIn,
|
|
182
186
|
})
|
|
183
187
|
: [];
|
|
@@ -28,7 +28,14 @@ export interface DeriveArchitectureOptions {
|
|
|
28
28
|
* hub — the billing action (#8217 D9, a reviewed change to the #7873 rule).
|
|
29
29
|
*/
|
|
30
30
|
export declare const SPOKE_CHECKOUT_ROUTE_FILES: readonly string[];
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* The spoke files that may import @working-theory/spoke-checkout to READ a
|
|
33
|
+
* paid session, and nothing else: the thank-you page's `readPaidCheckoutEmail`
|
|
34
|
+
* (#8757 D13). Kept apart from SPOKE_CHECKOUT_ROUTE_FILES so it does not
|
|
35
|
+
* also admit them to `lib/checkout`, the one file that fulfils an order.
|
|
36
|
+
*/
|
|
37
|
+
export declare const SPOKE_CHECKOUT_READER_FILES: readonly string[];
|
|
38
|
+
/** The forbidden patterns a spoke's apps/web carries (#7873, brief Part 2 §2; #8748). */
|
|
32
39
|
export declare const SPOKE_WEB_FORBIDDEN_PATTERNS: readonly ForbiddenPattern[];
|
|
33
40
|
/** The same three for a spoke's kept apps/mcp — no MCP file may import the package (#7873). */
|
|
34
41
|
export declare const SPOKE_MCP_FORBIDDEN_PATTERNS: readonly ForbiddenPattern[];
|
|
@@ -37,6 +37,15 @@ export const SPOKE_CHECKOUT_ROUTE_FILES = Object.freeze([
|
|
|
37
37
|
"apps/web/app/api/webhooks/stripe/route.ts",
|
|
38
38
|
"apps/web/app/[locale]/(authed)/account/billing/spoke-billing.action.ts",
|
|
39
39
|
]);
|
|
40
|
+
/**
|
|
41
|
+
* The spoke files that may import @working-theory/spoke-checkout to READ a
|
|
42
|
+
* paid session, and nothing else: the thank-you page's `readPaidCheckoutEmail`
|
|
43
|
+
* (#8757 D13). Kept apart from SPOKE_CHECKOUT_ROUTE_FILES so it does not
|
|
44
|
+
* also admit them to `lib/checkout`, the one file that fulfils an order.
|
|
45
|
+
*/
|
|
46
|
+
export const SPOKE_CHECKOUT_READER_FILES = Object.freeze([
|
|
47
|
+
"apps/web/app/[locale]/checkout/thank-you/page.tsx",
|
|
48
|
+
]);
|
|
40
49
|
/**
|
|
41
50
|
* A line-level regex for an import of `specifier` or one of its subpaths, in
|
|
42
51
|
* every form the line can take: `from "…"` (a static or re-export, including
|
|
@@ -53,7 +62,7 @@ function specifierForm(specifier) {
|
|
|
53
62
|
/** P1 with no exclusions — a kept apps/mcp's form; apps/web adds the route files. */
|
|
54
63
|
const SPOKE_CHECKOUT_PATTERN = Object.freeze({
|
|
55
64
|
pattern: specifierForm("@working-theory/spoke-checkout"),
|
|
56
|
-
reason: "Spoke checkout: only the buy route, the webhook route
|
|
65
|
+
reason: "Spoke checkout: only the buy route, the webhook route, the billing action and the thank-you page may import @working-theory/spoke-checkout (brief Part 2 §2, #7873, #8217, #8757)",
|
|
57
66
|
});
|
|
58
67
|
const STRIPE_CLIENT_PATTERN = Object.freeze({
|
|
59
68
|
pattern: specifierForm("@working-theory/stripe-client"),
|
|
@@ -63,11 +72,37 @@ const STRIPE_SDK_PATTERN = Object.freeze({
|
|
|
63
72
|
pattern: specifierForm("stripe"),
|
|
64
73
|
reason: "A spoke never imports the stripe SDK directly — it reaches Stripe only through @working-theory/spoke-checkout (#7873)",
|
|
65
74
|
});
|
|
66
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* The standard spoke checkout's fulfilment (`lib/checkout.ts`) sends the one
|
|
77
|
+
* `Idempotency-Key` the hub reads as a purchase, so only the files allowed to
|
|
78
|
+
* import the package may reach it (#8748 D3, SEC-8748-A): any relative or
|
|
79
|
+
* `@/`-aliased import (apps/web's tsconfig maps `@/*` to the app root) whose
|
|
80
|
+
* last segment is `checkout` (with or without a `.js`/`.ts`-family
|
|
81
|
+
* extension, which `bundler` resolution maps to the same file), in every form
|
|
82
|
+
* `specifierForm` covers. The
|
|
83
|
+
* closing quote straight after `checkout` keeps `checkout-config` and
|
|
84
|
+
* `checkout.actions` out of it. The checkout tests the writer copies are
|
|
85
|
+
* excluded too: the scaffold's lint reads test files.
|
|
86
|
+
*/
|
|
87
|
+
const SPOKE_FULFILMENT_PATTERN = Object.freeze({
|
|
88
|
+
pattern: "\\b(?:from|import|require)\\s*\\(?\\s*['\"`](?:(?:\\.{1,2}/)+|@/)(?:[^'\"`]*/)?checkout(?:\\.[cm]?[jt]sx?)?['\"`]",
|
|
89
|
+
paths_excluded: [
|
|
90
|
+
...SPOKE_CHECKOUT_ROUTE_FILES,
|
|
91
|
+
"apps/web/app/api/checkout/__tests__/**",
|
|
92
|
+
"apps/web/app/api/webhooks/stripe/__tests__/**",
|
|
93
|
+
"apps/web/lib/__tests__/checkout.test.ts",
|
|
94
|
+
],
|
|
95
|
+
reason: "Spoke checkout: only the buy route, the webhook route and the billing action may import lib/checkout, the one file that sends the hub an Idempotency-Key (#8748)",
|
|
96
|
+
});
|
|
97
|
+
/** The forbidden patterns a spoke's apps/web carries (#7873, brief Part 2 §2; #8748). */
|
|
67
98
|
export const SPOKE_WEB_FORBIDDEN_PATTERNS = Object.freeze([
|
|
68
|
-
Object.freeze({
|
|
99
|
+
Object.freeze({
|
|
100
|
+
...SPOKE_CHECKOUT_PATTERN,
|
|
101
|
+
paths_excluded: [...SPOKE_CHECKOUT_ROUTE_FILES, ...SPOKE_CHECKOUT_READER_FILES],
|
|
102
|
+
}),
|
|
69
103
|
STRIPE_CLIENT_PATTERN,
|
|
70
104
|
STRIPE_SDK_PATTERN,
|
|
105
|
+
SPOKE_FULFILMENT_PATTERN,
|
|
71
106
|
]);
|
|
72
107
|
/** The same three for a spoke's kept apps/mcp — no MCP file may import the package (#7873). */
|
|
73
108
|
export const SPOKE_MCP_FORBIDDEN_PATTERNS = Object.freeze([
|
package/dist/design-package.js
CHANGED
|
@@ -486,13 +486,14 @@ function validateStringMap(raw, file, field) {
|
|
|
486
486
|
}
|
|
487
487
|
const LAYOUT_KEYS = ["nav", "footer", "announcementBanner", "home"];
|
|
488
488
|
/** Every key a package's `layout` may carry — #7719 D3 adds the optional `deckGrounds` (a deck home only),
|
|
489
|
-
* #7721 D1 the optional `innerPages`, #7740 D1 the optional `deckPresentation` (a deck home only),
|
|
489
|
+
* #7721 D1 the optional `innerPages`, #7740 D1 the optional `deckPresentation` (a deck home only), #8773 the optional `deckAnchors` (a presenting deck only),
|
|
490
490
|
* #7742 D1 the optional `colorMode` and `chromeStyle`, #7797 D1 the optional `closingFooterPaths`. */
|
|
491
491
|
const ALLOWED_LAYOUT_KEYS = new Set([
|
|
492
492
|
...LAYOUT_KEYS,
|
|
493
493
|
"deckGrounds",
|
|
494
494
|
"innerPages",
|
|
495
495
|
"deckPresentation",
|
|
496
|
+
"deckAnchors",
|
|
496
497
|
"colorMode",
|
|
497
498
|
"chromeStyle",
|
|
498
499
|
"closingFooterPaths",
|
|
@@ -524,11 +525,12 @@ function validateLayout(raw, file) {
|
|
|
524
525
|
// `ResolvedSiteChrome` shape; an explicit "default" is the absent key.
|
|
525
526
|
// #7740 D1 — and `deckPresentation` only as `true`.
|
|
526
527
|
// #7742 D1 — and `colorMode` only as "light", `chromeStyle` only as "minimal".
|
|
527
|
-
const { innerPages, deckPresentation, colorMode, chromeStyle, ...rest } = validated;
|
|
528
|
+
const { innerPages, deckPresentation, deckAnchors, colorMode, chromeStyle, ...rest } = validated;
|
|
528
529
|
const layout = {
|
|
529
530
|
...rest,
|
|
530
531
|
...(innerPages === "bare" ? { innerPages } : {}),
|
|
531
532
|
...(deckPresentation === true ? { deckPresentation } : {}),
|
|
533
|
+
...(deckAnchors === "bare" ? { deckAnchors } : {}),
|
|
532
534
|
...(colorMode === "light" ? { colorMode } : {}),
|
|
533
535
|
...(chromeStyle === "minimal" ? { chromeStyle } : {}),
|
|
534
536
|
};
|
|
@@ -538,6 +540,9 @@ function validateLayout(raw, file) {
|
|
|
538
540
|
if (deckPresentation === true && layout.home !== "deck") {
|
|
539
541
|
refuse(file, "layout.deckPresentation", "applies only to home: deck");
|
|
540
542
|
}
|
|
543
|
+
if (deckAnchors === "bare" && deckPresentation !== true) {
|
|
544
|
+
refuse(file, "layout.deckAnchors", "applies only with deckPresentation: true");
|
|
545
|
+
}
|
|
541
546
|
return layout;
|
|
542
547
|
}
|
|
543
548
|
const TREATMENTS_ENUMS = {
|
package/dist/lib/env-catalog.js
CHANGED
|
@@ -52,7 +52,7 @@ export const ENV_CATALOG = {
|
|
|
52
52
|
secret: false,
|
|
53
53
|
},
|
|
54
54
|
AILK_LEAD_SITE: {
|
|
55
|
-
description: "
|
|
55
|
+
description: "This site's slug, written as payload.site on every lead its /contact form and MCP server capture, so a hub can tell its sites apart; unset leaves the lead unmarked.",
|
|
56
56
|
secret: false,
|
|
57
57
|
},
|
|
58
58
|
AILK_LICENSE_STORAGE_PATH: {
|
|
@@ -71,6 +71,10 @@ export const ENV_CATALOG = {
|
|
|
71
71
|
description: "The scope captured leads are stamped with; on a single-tenant install, the default workspace id.",
|
|
72
72
|
secret: false,
|
|
73
73
|
},
|
|
74
|
+
AILK_SPOKE_KEY: {
|
|
75
|
+
description: "The hub-issued key this site sends as x-ailk-spoke-key; the checkout fulfilment refuses to run without it. Never committed.",
|
|
76
|
+
secret: true,
|
|
77
|
+
},
|
|
74
78
|
AILK_VISITOR_FORWARD_KEY: {
|
|
75
79
|
description: "This spoke's key for forwarding each visitor's IP to the hub, so the hub rate-limits per visitor. Server-only.",
|
|
76
80
|
secret: true,
|
|
@@ -1813,8 +1813,10 @@ export const MODULE_SLICES = {
|
|
|
1813
1813
|
// the flow results pages, dev/purchase — spoke-intake Rule 3). What lands
|
|
1814
1814
|
// is the scaffold-commerce skill, the commerce-boundary rule and the
|
|
1815
1815
|
// @working-theory/ui-commerce dependency (`patches` below); the spoke's own
|
|
1816
|
-
// checkout —
|
|
1817
|
-
//
|
|
1816
|
+
// checkout — the standard spoke checkout, `apps/web` template source
|
|
1817
|
+
// (two routes, lib/checkout.ts, checkout-config.ts, two return pages and
|
|
1818
|
+
// their tests) — is copied by `writeSpokeCheckoutSlice`
|
|
1819
|
+
// (`spoke-checkout.ts`, #8748), not carried here.
|
|
1818
1820
|
spoke: true,
|
|
1819
1821
|
paidSectionsPackage: "@working-theory/ui-commerce",
|
|
1820
1822
|
apps: {
|
package/dist/programmatic.js
CHANGED
|
@@ -44,7 +44,7 @@ import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-c
|
|
|
44
44
|
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
|
-
import { spokeCheckoutSite, spokeEnvRowOptions, writeSpokeCheckoutSlice, } from "./spoke-checkout.js";
|
|
47
|
+
import { SPOKE_CHECKOUT_TEMPLATE_PATHS, dropSpokeCheckoutDependency, spokeCheckoutSite, spokeEnvRowOptions, writeSpokeCheckoutSlice, } from "./spoke-checkout.js";
|
|
48
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";
|
|
@@ -182,6 +182,9 @@ export function scaffoldSync(opts, copied) {
|
|
|
182
182
|
...(spoke !== undefined
|
|
183
183
|
? spokeExclusions(TEMPLATES_DIR, spoke)
|
|
184
184
|
: SPOKE_ONLY_FILES),
|
|
185
|
+
// #8748 D3 — the standard spoke checkout lands only through
|
|
186
|
+
// `writeSpokeCheckoutSlice`, on a spoke carrying commerce.
|
|
187
|
+
...SPOKE_CHECKOUT_TEMPLATE_PATHS,
|
|
185
188
|
],
|
|
186
189
|
markers: buildScaffoldMarkers(projectName),
|
|
187
190
|
...(copied !== undefined
|
|
@@ -193,6 +196,9 @@ export function scaffoldSync(opts, copied) {
|
|
|
193
196
|
}
|
|
194
197
|
: {}),
|
|
195
198
|
});
|
|
199
|
+
// #8748 D3 — the copied apps/web depends on the checkout package; only a
|
|
200
|
+
// commerce spoke keeps it, re-added by `writeSpokeCheckoutSlice` in scaffold().
|
|
201
|
+
dropSpokeCheckoutDependency(targetAbs);
|
|
196
202
|
// #7426 D5/D6 — the root files a spoke rewrites, and the spoke seams.
|
|
197
203
|
if (spoke !== undefined)
|
|
198
204
|
applySpokeTransforms(targetAbs, spoke);
|
|
@@ -793,18 +799,26 @@ export async function scaffold(opts) {
|
|
|
793
799
|
writeFileSync(rosterPath, manifestText);
|
|
794
800
|
}
|
|
795
801
|
}
|
|
796
|
-
// #7838 D8 — a commerce spoke's own checkout:
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
// code-route rows then record
|
|
802
|
+
// #7838 D8 — a commerce spoke's own checkout: the standard spoke checkout
|
|
803
|
+
// copied from the template (#8748 D3), its config stamped, once every layer
|
|
804
|
+
// has landed (so the spoke filter has already dropped the hub-checkout
|
|
805
|
+
// files) and before the manifest, whose code-route rows then record
|
|
806
|
+
// /checkout/thank-you and /checkout/unavailable. Each copied file is
|
|
807
|
+
// recorded like the base copy's, so `update` keeps it current.
|
|
800
808
|
// #8217 D8 — a spoke that signs in through its hub also gets the billing
|
|
801
809
|
// action and the web seam render that reaches it.
|
|
802
810
|
const spokeCommerce = spoke !== undefined && result.modules.includes("commerce");
|
|
803
811
|
if (spokeCommerce) {
|
|
804
812
|
writeSpokeCheckoutSlice(result.targetDir, {
|
|
805
813
|
site: spokeCheckoutSite(projectName),
|
|
814
|
+
projectName,
|
|
806
815
|
versions,
|
|
816
|
+
templatesDir: TEMPLATES_DIR,
|
|
807
817
|
signIn: spoke.signIn,
|
|
818
|
+
onWrite: (relPath, bytes) => {
|
|
819
|
+
if (isRecordable(relPath))
|
|
820
|
+
copied.set(relPath, sha256Hex(bytes));
|
|
821
|
+
},
|
|
808
822
|
});
|
|
809
823
|
}
|
|
810
824
|
// #6944 — the manifest records what this scaffold IS, once every layer has
|
package/dist/site-chrome.d.ts
CHANGED
|
@@ -41,6 +41,10 @@ export type SiteChromeHome = "default" | "deck";
|
|
|
41
41
|
/** The `innerPages` knob's vocabulary (#7721). `bare` → every non-home page
|
|
42
42
|
* gets the bare header and the one-bar footer. */
|
|
43
43
|
export type SiteChromeInnerPages = "default" | "bare";
|
|
44
|
+
/** The `deckAnchors` knob's vocabulary (#8773). `bare` → the deck home names
|
|
45
|
+
* its slides `#<id>` instead of `#slide-<id>`, keeping the `slide-` name only
|
|
46
|
+
* where the bare id would collide (the page renderer's `deckSlideAnchors`). */
|
|
47
|
+
export type SiteChromeDeckAnchors = "prefixed" | "bare";
|
|
44
48
|
/** The `colorMode` knob's vocabulary (#7742). `light` pins the site to light
|
|
45
49
|
* and removes every theme switch; dark then appears only as a deck ground. */
|
|
46
50
|
export type SiteChromeColorMode = "user" | "light";
|
|
@@ -72,6 +76,8 @@ export interface TemplateLayout {
|
|
|
72
76
|
innerPages?: SiteChromeInnerPages;
|
|
73
77
|
/** #7740 D1 — `true`: the deck home presents slide by slide (a deck home only). */
|
|
74
78
|
deckPresentation?: boolean;
|
|
79
|
+
/** #8773 — `bare`: a presenting deck names its slides `#<id>` (a presenting deck home only). */
|
|
80
|
+
deckAnchors?: SiteChromeDeckAnchors;
|
|
75
81
|
/** #7742 D1 — `light`: the site is pinned to light, with no theme switch. */
|
|
76
82
|
colorMode?: SiteChromeColorMode;
|
|
77
83
|
/** #7742 D1 — `minimal`: the minimal closing slide, consent bar and themed 404. */
|
|
@@ -91,6 +97,8 @@ export interface ResolvedSiteChrome {
|
|
|
91
97
|
innerPages?: "bare";
|
|
92
98
|
/** Present only when it resolves to `true` AND `home` is `deck` (#7740 D1) — never `false`. */
|
|
93
99
|
deckPresentation?: true;
|
|
100
|
+
/** Present only when it resolves to "bare" on a presenting deck home (#8773) — never "prefixed". */
|
|
101
|
+
deckAnchors?: "bare";
|
|
94
102
|
/** Present only when it resolves to "light" — never "user" (#7742 D1). */
|
|
95
103
|
colorMode?: "light";
|
|
96
104
|
/** Present only when it resolves to "minimal" — never "default" (#7742 D1). */
|
package/dist/site-chrome.js
CHANGED
|
@@ -64,6 +64,7 @@ const NAV_VALUES = ["full", "mark-only"];
|
|
|
64
64
|
const FOOTER_VALUES = ["columns", "centered"];
|
|
65
65
|
const HOME_VALUES = ["default", "deck"];
|
|
66
66
|
const INNER_PAGES_VALUES = ["default", "bare"];
|
|
67
|
+
const DECK_ANCHORS_VALUES = ["prefixed", "bare"];
|
|
67
68
|
const COLOR_MODE_VALUES = ["user", "light"];
|
|
68
69
|
const CHROME_STYLE_VALUES = ["default", "minimal"];
|
|
69
70
|
/**
|
|
@@ -140,6 +141,12 @@ export function validateTemplateLayout(value, subject) {
|
|
|
140
141
|
}
|
|
141
142
|
result.deckPresentation = raw.deckPresentation;
|
|
142
143
|
}
|
|
144
|
+
if ("deckAnchors" in raw && raw.deckAnchors !== undefined) {
|
|
145
|
+
if (!DECK_ANCHORS_VALUES.includes(raw.deckAnchors)) {
|
|
146
|
+
throw new TemplateLayoutError(`${subject}: layout.deckAnchors must be one of ${DECK_ANCHORS_VALUES.join(", ")} — got ${JSON.stringify(raw.deckAnchors)}`);
|
|
147
|
+
}
|
|
148
|
+
result.deckAnchors = raw.deckAnchors;
|
|
149
|
+
}
|
|
143
150
|
if ("colorMode" in raw && raw.colorMode !== undefined) {
|
|
144
151
|
if (!COLOR_MODE_VALUES.includes(raw.colorMode)) {
|
|
145
152
|
throw new TemplateLayoutError(`${subject}: layout.colorMode must be one of ${COLOR_MODE_VALUES.join(", ")} — got ${JSON.stringify(raw.colorMode)}`);
|
|
@@ -206,6 +213,11 @@ export function resolveSiteChrome(layout, designLayout) {
|
|
|
206
213
|
if ((layout?.deckPresentation ?? fromDesign.deckPresentation) === true && resolved.home === "deck") {
|
|
207
214
|
resolved.deckPresentation = true;
|
|
208
215
|
}
|
|
216
|
+
// #8773 — only "bare" is ever a key, and only where the deck presents: the
|
|
217
|
+
// slide anchors it names exist only under the presentation dial.
|
|
218
|
+
if ((layout?.deckAnchors ?? fromDesign.deckAnchors) === "bare" && resolved.deckPresentation === true) {
|
|
219
|
+
resolved.deckAnchors = "bare";
|
|
220
|
+
}
|
|
209
221
|
// #7742 D1 — as with `innerPages`, only the opt-in value is ever a key.
|
|
210
222
|
if ((layout?.colorMode ?? fromDesign.colorMode) === "light") {
|
|
211
223
|
resolved.colorMode = "light";
|
|
@@ -245,6 +257,8 @@ export function renderSiteChrome(resolved) {
|
|
|
245
257
|
// #7740 D1 — `innerPages`' form: the member and bullet always, the value
|
|
246
258
|
// line only when the chrome presents.
|
|
247
259
|
const presentationValue = resolved.deckPresentation === true ? "\n deckPresentation: true," : "";
|
|
260
|
+
// #8773 — the same form: member and bullet always, the value line only for "bare".
|
|
261
|
+
const deckAnchorsValue = resolved.deckAnchors === "bare" ? '\n deckAnchors: "bare",' : "";
|
|
248
262
|
// #7742 D1 — the same form again: member and bullet always, value lines
|
|
249
263
|
// only for the opt-in values.
|
|
250
264
|
const colorModeValue = resolved.colorMode === "light" ? '\n colorMode: "light",' : "";
|
|
@@ -271,6 +285,7 @@ export function renderSiteChrome(resolved) {
|
|
|
271
285
|
// - innerPages: "bare" → every non-home page renders the bare header and
|
|
272
286
|
// the one-bar footer.
|
|
273
287
|
// - deckPresentation → the deck home presents slide by slide: fitted slides, slide anchors, the mark follows the ground
|
|
288
|
+
// - deckAnchors: "bare" → the presenting deck names its slides #<id> instead of #slide-<id>
|
|
274
289
|
// - colorMode: "light" → the site is pinned to light and shows no theme
|
|
275
290
|
// switch; dark appears only as a deck ground.
|
|
276
291
|
// - chromeStyle: "minimal" → the deck's closing slide is the wordmark,
|
|
@@ -286,6 +301,7 @@ export interface SiteChrome {
|
|
|
286
301
|
home: "default" | "deck";
|
|
287
302
|
innerPages?: "bare";
|
|
288
303
|
deckPresentation?: true;
|
|
304
|
+
deckAnchors?: "bare";
|
|
289
305
|
colorMode?: "light";
|
|
290
306
|
chromeStyle?: "minimal";
|
|
291
307
|
closingFooterPaths?: readonly string[];${groundsMember}
|
|
@@ -295,7 +311,7 @@ export const siteChrome: SiteChrome = {
|
|
|
295
311
|
nav: ${JSON.stringify(resolved.nav)},
|
|
296
312
|
footer: ${JSON.stringify(resolved.footer)},
|
|
297
313
|
announcementBanner: ${JSON.stringify(resolved.announcementBanner)},
|
|
298
|
-
home: ${JSON.stringify(resolved.home)},${groundsValue}${innerPagesValue}${presentationValue}${colorModeValue}${chromeStyleValue}${closingFooterPathsValue}
|
|
314
|
+
home: ${JSON.stringify(resolved.home)},${groundsValue}${innerPagesValue}${presentationValue}${deckAnchorsValue}${colorModeValue}${chromeStyleValue}${closingFooterPathsValue}
|
|
299
315
|
};
|
|
300
316
|
`;
|
|
301
317
|
}
|
package/dist/spoke-checkout.d.ts
CHANGED
|
@@ -4,17 +4,17 @@ import { type WorkspaceVersionMap } from "./substitute.js";
|
|
|
4
4
|
import { type SignInMode } from "./topology.js";
|
|
5
5
|
/** The checkout package a spoke's two route files import. */
|
|
6
6
|
export declare const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
|
|
7
|
-
/** The site's checkout data — written once, the site's from then on. */
|
|
7
|
+
/** The site's checkout data — 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
9
|
/** The two return pages: Stripe's success URL and every refused buy's target. */
|
|
10
10
|
export declare const SPOKE_CHECKOUT_PAGE_FILES: readonly string[];
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The standard spoke checkout's template paths, scaffold-relative (#8748 D3).
|
|
13
|
+
* A directory entry covers every file under it, so the tests that sit beside
|
|
14
|
+
* the routes and pages travel with them. `copyTemplate` skips all of them for
|
|
15
|
+
* every scaffold; `writeSpokeCheckoutSlice` copies them into a commerce spoke.
|
|
16
16
|
*/
|
|
17
|
-
export declare const
|
|
17
|
+
export declare const SPOKE_CHECKOUT_TEMPLATE_PATHS: readonly string[];
|
|
18
18
|
/** The variables a commerce spoke depends on (D9). The package reads three of them. */
|
|
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). */
|
|
@@ -54,29 +54,50 @@ export declare const E2E_ACTION_PINS: Readonly<Record<string, string>>;
|
|
|
54
54
|
*/
|
|
55
55
|
export declare const CHECKOUT_E2E_WORKFLOW: string;
|
|
56
56
|
/**
|
|
57
|
-
* The slice for `site
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
57
|
+
* The rendered part of the slice for `site` (#8748 D3): with `signIn: "hub"`
|
|
58
|
+
* the billing action (#8217 D8), then the checkout e2e workflow (#7839 D8).
|
|
59
|
+
* Neither depends on `site`. The rest of the slice is template source,
|
|
60
|
+
* copied by `writeSpokeCheckoutSlice`. Throws `TopologyError` for an invalid
|
|
61
|
+
* stamp (SEC-7838-10).
|
|
62
62
|
*/
|
|
63
63
|
export declare function renderSpokeCheckoutFiles(site: string, opts?: {
|
|
64
64
|
signIn?: SignInMode | undefined;
|
|
65
65
|
}): [string, string][];
|
|
66
66
|
/**
|
|
67
|
-
* Write the slice into `targetDir` (D3).
|
|
68
|
-
*
|
|
67
|
+
* Write the slice into `targetDir` (#7838 D3, #8748 D3). The standard's
|
|
68
|
+
* template files (`SPOKE_CHECKOUT_TEMPLATE_PATHS`) are copied from
|
|
69
|
+
* `templatesDir`, each rendered with `copyTemplate`'s own renderer and
|
|
70
|
+
* markers, so a recorded hash equals what `create-ailk update` renders; the
|
|
71
|
+
* config's one `const SITE` line is stamped with `site`. `onWrite` is called
|
|
72
|
+
* once per template file written, as `copyTemplate` calls it. Then come the
|
|
73
|
+
* rendered files (`renderSpokeCheckoutFiles`). A file that already exists is
|
|
74
|
+
* left byte-for-byte and reported `skipped` — the config is the site's once
|
|
69
75
|
* written. With `signIn: "hub"` (#8217 D8) the slice carries the billing
|
|
70
76
|
* action, and the web seam — generated, so always rewritten — becomes the
|
|
71
|
-
* billing render: reported `patched` when it changed, not at all when equal.
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
77
|
+
* billing render: reported `patched` when it changed, not at all when equal.
|
|
78
|
+
* Adds the package to `apps/web/package.json` when absent, and raises a plain
|
|
79
|
+
* pin older than the one it writes (#7839): the generated checkout e2e
|
|
80
|
+
* workflow runs a command a `^0.2.0` pin never reaches. Any other range —
|
|
81
|
+
* newer, `workspace:`, a tag — is the site's and is kept.
|
|
75
82
|
* Every refusal (an invalid stamp, a version map without the package, a
|
|
76
|
-
* missing or malformed `apps/web/package.json
|
|
83
|
+
* missing or malformed `apps/web/package.json`, a template missing any
|
|
84
|
+
* `SPOKE_CHECKOUT_TEMPLATE_PATHS` entry, a template config without exactly
|
|
85
|
+
* one `const SITE` line, a destination to write that resolves outside
|
|
86
|
+
* `targetDir`) throws before the first write.
|
|
77
87
|
*/
|
|
78
88
|
export declare function writeSpokeCheckoutSlice(targetDir: string, opts: {
|
|
79
89
|
site: string;
|
|
90
|
+
projectName: string;
|
|
80
91
|
versions: WorkspaceVersionMap;
|
|
92
|
+
templatesDir: string;
|
|
81
93
|
signIn?: SignInMode | undefined;
|
|
94
|
+
onWrite?: (relPath: string, bytes: Uint8Array) => void;
|
|
82
95
|
}): InstallReportRow[];
|
|
96
|
+
/**
|
|
97
|
+
* Drop the checkout package from a scaffold that does not get the slice — a
|
|
98
|
+
* hub, or a spoke without `commerce` (#8748 D3). The template's
|
|
99
|
+
* `apps/web/package.json` depends on it because the monorepo's `apps/web`
|
|
100
|
+
* carries the standard. Every other key is kept in place; a site without the
|
|
101
|
+
* dependency, or without `apps/web/package.json`, is left as it is.
|
|
102
|
+
*/
|
|
103
|
+
export declare function dropSpokeCheckoutDependency(targetDir: string): void;
|