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
|
@@ -46,17 +46,14 @@ export const CLAIM_GATED_ROUTES = {
|
|
|
46
46
|
* `CLAIM_GATED_ROUTES`, a route absent from the route table is not stale: the
|
|
47
47
|
* module that owns it may simply not be installed in this leg.
|
|
48
48
|
*/
|
|
49
|
-
export const CONFIG_GATED_ROUTES = {
|
|
50
|
-
"/meet": "SCHEDULING_CAL_LINK",
|
|
51
|
-
};
|
|
49
|
+
export const CONFIG_GATED_ROUTES = {};
|
|
52
50
|
/**
|
|
53
51
|
* The placeholder heading each `CONFIG_GATED_ROUTES` entry renders while its
|
|
54
|
-
* env var is unset
|
|
55
|
-
*
|
|
52
|
+
* env var is unset. Empty since #8843 D14: `/meet` reads the workspace's
|
|
53
|
+
* meetings and answers 200 with its own H1 unconfigured, so no route is
|
|
54
|
+
* config-gated today.
|
|
56
55
|
*/
|
|
57
|
-
export const CONFIG_GATED_PLACEHOLDERS = {
|
|
58
|
-
"/meet": "{Book a time with us}",
|
|
59
|
-
};
|
|
56
|
+
export const CONFIG_GATED_PLACEHOLDERS = {};
|
|
60
57
|
const LOOPBACK_BASE = "http://localhost";
|
|
61
58
|
function pathnameOf(location, relativeTo) {
|
|
62
59
|
return new URL(location, new URL(relativeTo, LOOPBACK_BASE)).pathname;
|
|
@@ -793,6 +793,9 @@ export const MODULE_SLICES = {
|
|
|
793
793
|
"apps/api/src/routes/workspaces/index.ts",
|
|
794
794
|
"apps/api/src/routes/workspaces/sessions.ts",
|
|
795
795
|
"apps/api/src/routes/workspaces/config.ts",
|
|
796
|
+
// #8926 D1 — the workspace Offerings routes, registered in the
|
|
797
|
+
// multi-tenant tenant scope beside the billing routes.
|
|
798
|
+
"apps/api/src/routes/workspaces/offerings.ts",
|
|
796
799
|
// #8438 D8 — the Admin owner's consents to Working Theory: registered
|
|
797
800
|
// under `isModuleEnabled("multi-tenant")` alone, the module that
|
|
798
801
|
// contributes the Admin app.
|
|
@@ -805,6 +808,8 @@ export const MODULE_SLICES = {
|
|
|
805
808
|
"apps/api/src/routes/billing/index.ts",
|
|
806
809
|
"apps/api/src/routes/billing/portal.ts",
|
|
807
810
|
"apps/api/src/routes/billing/read.ts",
|
|
811
|
+
"apps/api/src/routes/billing/stripe-connection.ts",
|
|
812
|
+
"apps/api/src/routes/billing/stripe-products.ts",
|
|
808
813
|
"apps/api/src/routes/billing/usage.ts",
|
|
809
814
|
"apps/api/src/routes/waitlist-experiments/index.ts",
|
|
810
815
|
"apps/api/src/routes/waitlist-experiments/list.ts",
|
|
@@ -854,6 +859,10 @@ export const MODULE_SLICES = {
|
|
|
854
859
|
"apps/api/src/routes/workspaces/submissions.ts",
|
|
855
860
|
"apps/api/src/routes/workspaces/subscribers.ts",
|
|
856
861
|
"apps/api/src/services/inbox-submissions.ts",
|
|
862
|
+
// #9170 — the Inbox orders read: the same gate and the same
|
|
863
|
+
// tenant-db.ts import, co-owned with `workspace` for the same reason.
|
|
864
|
+
"apps/api/src/routes/workspaces/orders.ts",
|
|
865
|
+
"apps/api/src/services/inbox-orders.ts",
|
|
857
866
|
// #852 D8 — the People reads: the same workspace||multi-tenant gate and
|
|
858
867
|
// the same tenant-db.ts import, co-owned with `workspace` for the same
|
|
859
868
|
// reason.
|
|
@@ -1023,6 +1032,9 @@ export const MODULE_SLICES = {
|
|
|
1023
1032
|
"apps/api/src/routes/workspaces/submissions.ts",
|
|
1024
1033
|
"apps/api/src/routes/workspaces/subscribers.ts",
|
|
1025
1034
|
"apps/api/src/services/inbox-submissions.ts",
|
|
1035
|
+
// Co-owned with `multi-tenant` (#9170), as the reads above.
|
|
1036
|
+
"apps/api/src/routes/workspaces/orders.ts",
|
|
1037
|
+
"apps/api/src/services/inbox-orders.ts",
|
|
1026
1038
|
// Co-owned with `multi-tenant` (#852 D8), as the reads above.
|
|
1027
1039
|
"apps/api/src/routes/workspaces/people.ts",
|
|
1028
1040
|
// Co-owned with `multi-tenant` (#7877 D9, D11) — see that slice's entry.
|
|
@@ -1134,8 +1146,16 @@ export const MODULE_SLICES = {
|
|
|
1134
1146
|
pattern: CAL_DOMAIN_PATTERN,
|
|
1135
1147
|
// The two server-side apps: apps/api holds the provider client, and
|
|
1136
1148
|
// apps/mcp's tools name booking links in their fixtures. Everything
|
|
1137
|
-
// else — apps/web above all — is browser-reachable.
|
|
1138
|
-
|
|
1149
|
+
// else — apps/web above all — is browser-reachable. The two web files
|
|
1150
|
+
// are the booking pages' CSP (#8843 D12): the policy file names the
|
|
1151
|
+
// embed's origin only to admit it on those pages, and the middleware
|
|
1152
|
+
// applies it. Same carve-out as the source monorepo's architecture.yaml.
|
|
1153
|
+
paths_excluded: [
|
|
1154
|
+
"apps/api/**",
|
|
1155
|
+
"apps/mcp/**",
|
|
1156
|
+
"apps/web/security-headers.config.ts",
|
|
1157
|
+
"apps/web/middleware.ts",
|
|
1158
|
+
],
|
|
1139
1159
|
reason: "Only this site's server tier addresses the provider; every browser call goes to this site's own /v1/schedule/* routes instead",
|
|
1140
1160
|
},
|
|
1141
1161
|
{
|
|
@@ -1224,10 +1244,22 @@ export const MODULE_SLICES = {
|
|
|
1224
1244
|
"apps/api/src/services/scheduling/__tests__/cal-provider.test.ts",
|
|
1225
1245
|
"apps/mcp/src/tools/list_availability.ts",
|
|
1226
1246
|
"apps/mcp/src/tools/get_event_meta.ts",
|
|
1247
|
+
// #8843 D5 — the twin of GET /v1/schedule/meetings.
|
|
1248
|
+
"apps/mcp/src/tools/list_meetings.ts",
|
|
1227
1249
|
"apps/mcp/src/tools/create_booking.ts",
|
|
1228
1250
|
"apps/web/app/[locale]/meet/page.tsx",
|
|
1229
1251
|
"apps/web/app/[locale]/meet/[slug]/page.tsx",
|
|
1230
1252
|
"apps/web/app/[locale]/meet/confirmed/page.tsx",
|
|
1253
|
+
// #8843 D6–D8 — the booking pages' one builder, the index frame
|
|
1254
|
+
// `/meet` and `/private` share, the private index, and their suites,
|
|
1255
|
+
// individually (§ D12, property 1: a glob ships no test).
|
|
1256
|
+
"apps/web/lib/meetings.tsx",
|
|
1257
|
+
"apps/web/lib/__tests__/meetings.test.tsx",
|
|
1258
|
+
"apps/web/lib/__tests__/meetings.fixtures.ts",
|
|
1259
|
+
"apps/web/app/[locale]/meet/meeting-index.tsx",
|
|
1260
|
+
"apps/web/app/[locale]/private/page.tsx",
|
|
1261
|
+
"apps/web/app/[locale]/private/__tests__/page.test.tsx",
|
|
1262
|
+
"apps/web/app/[locale]/private/__tests__/sitemap.test.ts",
|
|
1231
1263
|
// #6085 (brief H7) — the six files the webhook receiver + the
|
|
1232
1264
|
// tenant-guarded appointments read add. Individually, never as a
|
|
1233
1265
|
// glob (§ D12 / this card's own comment above, property 1: a glob
|
|
@@ -1269,6 +1301,9 @@ export const MODULE_SLICES = {
|
|
|
1269
1301
|
"apps/api/src/lib/public-meetings-config.ts",
|
|
1270
1302
|
"apps/api/src/routes/schedule/meeting-types.ts",
|
|
1271
1303
|
"apps/api/src/routes/schedule/__tests__/meeting-types.test.ts",
|
|
1304
|
+
// #8843 D1 — the meetings read's own suite; the route is in
|
|
1305
|
+
// `routes/schedule/index.ts`, listed above.
|
|
1306
|
+
"apps/api/src/routes/schedule/__tests__/meetings.test.ts",
|
|
1272
1307
|
// #7532 — a hub spoke's meeting types and booking, through the real
|
|
1273
1308
|
// scheduling routes; it ships and strips with them.
|
|
1274
1309
|
"apps/api/src/routes/schedule/__tests__/spoke.test.ts",
|
|
@@ -1301,6 +1336,10 @@ export const MODULE_SLICES = {
|
|
|
1301
1336
|
"apps/api/src/services/scheduling/resolve.ts",
|
|
1302
1337
|
"apps/api/src/services/scheduling/__tests__/connections.test.ts",
|
|
1303
1338
|
"apps/api/src/services/scheduling/__tests__/provider-resolution.test.ts",
|
|
1339
|
+
// #9129 — the meetings-list assembly the meetings route and the member
|
|
1340
|
+
// meeting types share, plus its suite.
|
|
1341
|
+
"apps/api/src/services/scheduling/meetings.ts",
|
|
1342
|
+
"apps/api/src/services/scheduling/__tests__/meetings.test.ts",
|
|
1304
1343
|
"apps/api/src/routes/workspaces/__tests__/meetings-connection.test.ts",
|
|
1305
1344
|
"apps/api/src/routes/schedule/__tests__/public-connection.test.ts",
|
|
1306
1345
|
"apps/api/src/routes/webhooks/__tests__/calcom-connection.test.ts",
|
|
@@ -1323,14 +1362,16 @@ export const MODULE_SLICES = {
|
|
|
1323
1362
|
"apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts",
|
|
1324
1363
|
"apps/api/src/__tests__/openapi-keyless-hub.test.ts",
|
|
1325
1364
|
],
|
|
1326
|
-
// pageContributions (spec 5478; parent § D6 shape 1) — the
|
|
1327
|
-
//
|
|
1328
|
-
// `ownedFiles` above already carries. No
|
|
1329
|
-
//
|
|
1365
|
+
// pageContributions (spec 5478; parent § D6 shape 1) — the meeting
|
|
1366
|
+
// index, the meeting page, the confirmation page and the private index
|
|
1367
|
+
// (#8843 D8), whose route files `ownedFiles` above already carries. No
|
|
1368
|
+
// row names seed content: all four render from the /v1/schedule/*
|
|
1369
|
+
// routes, not from an MDX file.
|
|
1330
1370
|
pageContributions: [
|
|
1331
1371
|
{ route: "/meet" },
|
|
1332
1372
|
{ route: "/meet/[slug]" },
|
|
1333
1373
|
{ route: "/meet/confirmed" },
|
|
1374
|
+
{ route: "/private" },
|
|
1334
1375
|
// #6086 — the authed appointments page. Bracket (roster) grammar and no
|
|
1335
1376
|
// `content` key, for the same reason the three /meet rows carry none:
|
|
1336
1377
|
// it renders from `/v1/schedule/appointments`, not from an MDX file.
|
|
@@ -1668,6 +1709,16 @@ export const MODULE_SLICES = {
|
|
|
1668
1709
|
// `NESTED_FOREIGN_MODULE_ENTRIES` for the companion fix that keeps its
|
|
1669
1710
|
// presence from falsely reading as "projects carried".
|
|
1670
1711
|
"apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx",
|
|
1712
|
+
// #8840 D11/D13 — the Job application magnet's two walks. Same kind as
|
|
1713
|
+
// `projects/apply` above: they sit under the UNRELATED "careers"
|
|
1714
|
+
// surface's URL segment, which `excludedRoutePaths` does not gate on
|
|
1715
|
+
// signal-collectors, so `ownedFiles` keeps them out of a keyless
|
|
1716
|
+
// scaffold, and `NESTED_FOREIGN_MODULE_ENTRIES` keeps their presence
|
|
1717
|
+
// from reading as "careers carried". `flows/[slug]/magnet-page.tsx`,
|
|
1718
|
+
// the renderer they mount, rides the `flows` surface's directory with
|
|
1719
|
+
// its siblings (the note above).
|
|
1720
|
+
"apps/web/app/[locale]/careers/apply/page.tsx",
|
|
1721
|
+
"apps/web/app/[locale]/careers/[slug]/apply/page.tsx",
|
|
1671
1722
|
// #5581 U4 — the eleven signal-collectors components + the slice's own
|
|
1672
1723
|
// re-export entry point moved to packages-paid/ui-signal-collectors;
|
|
1673
1724
|
// the package is delivered to a keyed scaffold via `patches` below,
|
|
@@ -1683,6 +1734,17 @@ export const MODULE_SLICES = {
|
|
|
1683
1734
|
// spec 6892 D2 — the /reports hub, alongside its reader above: also a
|
|
1684
1735
|
// URL segment MODULE_SURFACES["signal-collectors"] does not gate.
|
|
1685
1736
|
"apps/web/app/[locale]/reports/page.tsx",
|
|
1737
|
+
// #8848 D6, D7 — the hub deliverable at `/reports/<slug>`: its renderer,
|
|
1738
|
+
// the gated walk and the ungated file's byte proxy. Same segment, same
|
|
1739
|
+
// reason as the reader above.
|
|
1740
|
+
"apps/web/app/[locale]/reports/deliverable-page.tsx",
|
|
1741
|
+
"apps/web/app/[locale]/reports/[slug]/start/page.tsx",
|
|
1742
|
+
"apps/web/app/[locale]/reports/[slug]/file/route.ts",
|
|
1743
|
+
// #8848 D12 — `/downloads` redirects into `/reports`, so a scaffold
|
|
1744
|
+
// without this module ships no `/downloads` at all (the redirect target
|
|
1745
|
+
// is this module's) rather than the old broken list.
|
|
1746
|
+
"apps/web/app/[locale]/downloads/page.tsx",
|
|
1747
|
+
"apps/web/app/[locale]/downloads/[slug]/page.tsx",
|
|
1686
1748
|
//
|
|
1687
1749
|
// The two sections that route composes — `report-header` and
|
|
1688
1750
|
// `reports-list` — are deliberately NOT owned here. They are
|
|
@@ -1721,6 +1783,9 @@ export const MODULE_SLICES = {
|
|
|
1721
1783
|
// the module gets them — `moduleRosterRows` derives the code-route form
|
|
1722
1784
|
// for this row rather than requiring a template to ship one (D9).
|
|
1723
1785
|
{ route: "/reports/[slug]", contentDir: "content/en/ReportPage" },
|
|
1786
|
+
// #8848 D7's walk (`/reports/[slug]/start`) is NOT a contribution: a
|
|
1787
|
+
// contribution needs a shipped template manifest to carry its row, and
|
|
1788
|
+
// none does yet. Its code ships through `ownedFiles` above.
|
|
1724
1789
|
],
|
|
1725
1790
|
// installMap (#5618 D6.3) — the module's sample files, moved here out
|
|
1726
1791
|
// of `site-templates/marketing-site/` so the module OWNS its samples
|
|
@@ -1750,6 +1815,12 @@ export const MODULE_SLICES = {
|
|
|
1750
1815
|
// code of its own for either route.
|
|
1751
1816
|
// `generate-sample-report.py` keeps its root-level destination
|
|
1752
1817
|
// (finding 1 — AC7 forbids changing the Pro file set).
|
|
1818
|
+
// `diagnostic` and `join-waitlist` are the starter's two placeholder
|
|
1819
|
+
// flow seeds (#8850 D5): every visible string a `{brace}`, every key one
|
|
1820
|
+
// a spoke may seed, so `create-ailk seed-flows` sends them to a hub with
|
|
1821
|
+
// no edits once `spokeDestination` lands them at `flows/<slug>.json`. A
|
|
1822
|
+
// hub gets them as inert `database/flows/` files, as it gets
|
|
1823
|
+
// `fit-check` and `referral`.
|
|
1753
1824
|
installMap: {
|
|
1754
1825
|
"packages/create-ailk/module-templates/signal-collectors/content/en/ProjectsList/projects.mdx": "content/en/ProjectsList/projects.mdx",
|
|
1755
1826
|
"packages/create-ailk/module-templates/signal-collectors/content/en/Project/fit-check-funnel.mdx": "content/en/Project/fit-check-funnel.mdx",
|
|
@@ -1765,6 +1836,8 @@ export const MODULE_SLICES = {
|
|
|
1765
1836
|
"packages/create-ailk/module-templates/signal-collectors/content/en/LandingPage/referral.mdx": "content/en/LandingPage/referral.mdx",
|
|
1766
1837
|
"packages/create-ailk/module-templates/signal-collectors/flows/fit-check.flow.json": "database/flows/fit-check.flow.json",
|
|
1767
1838
|
"packages/create-ailk/module-templates/signal-collectors/flows/referral.flow.json": "database/flows/referral.flow.json",
|
|
1839
|
+
"packages/create-ailk/module-templates/signal-collectors/flows/diagnostic.flow.json": "database/flows/diagnostic.flow.json",
|
|
1840
|
+
"packages/create-ailk/module-templates/signal-collectors/flows/join-waitlist.flow.json": "database/flows/join-waitlist.flow.json",
|
|
1768
1841
|
"packages/create-ailk/module-templates/signal-collectors/waitlist/launch-waitlist.experiment.json": "database/waitlist/launch-waitlist.experiment.json",
|
|
1769
1842
|
"packages/create-ailk/module-templates/signal-collectors/reports/sample-report.pdf": "apps/web/public/reports/sample-report.pdf",
|
|
1770
1843
|
"packages/create-ailk/module-templates/signal-collectors/reports/generate-sample-report.py": "reports/generate-sample-report.py",
|
|
@@ -1812,10 +1885,10 @@ export const MODULE_SLICES = {
|
|
|
1812
1885
|
// checkout (`SPOKE_HUB_CHECKOUT_SEEDS`: flows/[slug]/checkout.actions.ts,
|
|
1813
1886
|
// the flow results pages, dev/purchase — spoke-intake Rule 3). What lands
|
|
1814
1887
|
// is the scaffold-commerce skill, the commerce-boundary rule and the
|
|
1815
|
-
// @working-theory/ui-commerce dependency (`patches` below)
|
|
1816
|
-
//
|
|
1817
|
-
//
|
|
1818
|
-
// their tests) —
|
|
1888
|
+
// @working-theory/ui-commerce dependency (`patches` below). A spoke sells
|
|
1889
|
+
// through its hub's Offerings buy route (#9142); its remaining checkout
|
|
1890
|
+
// files — `apps/web` template source (checkout-config.ts, two return
|
|
1891
|
+
// pages and their tests) — are copied by `writeSpokeCheckoutSlice`
|
|
1819
1892
|
// (`spoke-checkout.ts`, #8748), not carried here.
|
|
1820
1893
|
spoke: true,
|
|
1821
1894
|
paidSectionsPackage: "@working-theory/ui-commerce",
|
|
@@ -2138,6 +2211,18 @@ export function coOwnedModuleDestinations() {
|
|
|
2138
2211
|
// red the scaffold's database jest run. Matched exactly, not by basename
|
|
2139
2212
|
// in `EXCLUDE_FILES`, which would drop a `build.test.ts` anywhere in the
|
|
2140
2213
|
// snapshot; every other database suite still ships.
|
|
2214
|
+
// - the /pricing suite `monorepo-only.test.tsx` and its snapshot (#9179) —
|
|
2215
|
+
// it snapshots the monorepo's own `content/en/Pricing/pricing.mdx`, which
|
|
2216
|
+
// a scaffold replaces with its seed, and reads
|
|
2217
|
+
// `packages/api-client/src/offerings.ts`, which no scaffold carries. Only
|
|
2218
|
+
// those two assertions moved there; `unconnected.test.tsx` and
|
|
2219
|
+
// `offerings.test.tsx` keep every scaffold-meaningful case and ship.
|
|
2220
|
+
// - the Offerings `delegate-confinement.test.ts` (#9179) — it runs
|
|
2221
|
+
// `git grep -- apps apps-paid packages` from the repo root to prove the
|
|
2222
|
+
// `stripeConnection` and `offeringsCheckoutSession` delegates are each
|
|
2223
|
+
// named by one file. A scaffold is not a git repository. Only those two
|
|
2224
|
+
// cases moved there; `connection.test.ts` and
|
|
2225
|
+
// `checkout-session-record.test.ts` keep the stores' coverage and ship.
|
|
2141
2226
|
export const EXCLUDE_TEMPLATE_DIRS = [
|
|
2142
2227
|
"apps/web/__tests__",
|
|
2143
2228
|
"apps/web/app/__tests__",
|
|
@@ -2163,6 +2248,9 @@ export const EXCLUDE_TEMPLATE_DIRS = [
|
|
|
2163
2248
|
"apps/mcp/__tests__/parity.test.ts",
|
|
2164
2249
|
"apps/mcp/__tests__/audit.test.ts",
|
|
2165
2250
|
"database/__tests__/build.test.ts",
|
|
2251
|
+
"apps/web/app/[locale]/pricing/__tests__/monorepo-only.test.tsx",
|
|
2252
|
+
"apps/web/app/[locale]/pricing/__tests__/__snapshots__/monorepo-only.test.tsx.snap",
|
|
2253
|
+
"apps/api/src/services/offerings/__tests__/delegate-confinement.test.ts",
|
|
2166
2254
|
];
|
|
2167
2255
|
export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
|
|
2168
2256
|
const roots = new Set(snapshotRoots);
|
package/dist/paid-sections.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Scaffold-relative path of the generated registry. */
|
|
2
2
|
export declare const PAID_SECTIONS_PATH = "apps/web/lib/paid-sections.generated.ts";
|
|
3
|
+
/** Scaffold-relative path of the generated module-presence flags (#8843 D10). */
|
|
4
|
+
export declare const MODULE_FLAGS_PATH = "apps/web/lib/modules.generated.ts";
|
|
3
5
|
export interface PaidSectionRow {
|
|
4
6
|
module: string;
|
|
5
7
|
package: string;
|
|
@@ -15,9 +17,26 @@ export declare function paidSectionRows(): PaidSectionRow[];
|
|
|
15
17
|
* public strip drops the file (\`check-oss-strip.sh\` condition (e)).
|
|
16
18
|
*/
|
|
17
19
|
export declare function renderPaidSections(present: ReadonlySet<string>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Render the module-presence flags. Line 1 is `// oss: false` exactly when a
|
|
22
|
+
* flag is on, the registry's own rule: an `on` flag names pages the public
|
|
23
|
+
* tree does not carry.
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderModuleFlags(flags: {
|
|
26
|
+
schedulingPages: boolean;
|
|
27
|
+
}): string;
|
|
28
|
+
/**
|
|
29
|
+
* Write the module-presence flags into a scaffold from its own
|
|
30
|
+
* `apps/web/package.json` (#8843 D10) — never copied from a template, so a
|
|
31
|
+
* site without the scheduling module can never carry `true`. Answers whether
|
|
32
|
+
* the file was `added`, `updated` (changed bytes) or `unchanged`, or
|
|
33
|
+
* `undefined` (writing nothing) for a scaffold with no web app.
|
|
34
|
+
*/
|
|
35
|
+
export declare function writeModuleFlags(targetDir: string): "added" | "updated" | "unchanged" | undefined;
|
|
18
36
|
/**
|
|
19
37
|
* Write the registry into a scaffold, deriving presence from the scaffold's
|
|
20
|
-
* own `apps/web/package.json
|
|
21
|
-
* file or `apps/web/lib/` is
|
|
38
|
+
* own `apps/web/package.json`, and the module-presence flags beside it.
|
|
39
|
+
* Returns `false` (writing nothing) when that file or `apps/web/lib/` is
|
|
40
|
+
* absent — a scaffold with no web app.
|
|
22
41
|
*/
|
|
23
42
|
export declare function writePaidSections(targetDir: string): boolean;
|
package/dist/paid-sections.js
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
* every layer (snapshot transform, module `patches`, `add-module`) already
|
|
16
16
|
* keeps true. The monorepo's committed copy is the render with every row
|
|
17
17
|
* present and is pinned byte-equal by `__tests__/paid-sections.test.ts`.
|
|
18
|
+
*
|
|
19
|
+
* Its sibling, `apps/web/lib/modules.generated.ts` (#8843 D10), is the
|
|
20
|
+
* module-presence flag: `SCHEDULING_PAGES` is true exactly when the site
|
|
21
|
+
* carries the scheduling slice's UI package, so the locale layout drops the
|
|
22
|
+
* marketing chrome on the booking pages only where they exist. A separate
|
|
23
|
+
* file, importing nothing, because the registry's `import()`s would pull UI
|
|
24
|
+
* packages into any edge code that reads the flag.
|
|
18
25
|
*/
|
|
19
26
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
27
|
import { join } from "node:path";
|
|
@@ -22,6 +29,10 @@ import { MODULE_SLICES } from "./module-architecture.js";
|
|
|
22
29
|
import { RECOGNIZED_MODULES } from "./parse-args.js";
|
|
23
30
|
/** Scaffold-relative path of the generated registry. */
|
|
24
31
|
export const PAID_SECTIONS_PATH = "apps/web/lib/paid-sections.generated.ts";
|
|
32
|
+
/** Scaffold-relative path of the generated module-presence flags (#8843 D10). */
|
|
33
|
+
export const MODULE_FLAGS_PATH = "apps/web/lib/modules.generated.ts";
|
|
34
|
+
/** The dependency whose presence means the scheduling module's pages exist. */
|
|
35
|
+
const SCHEDULING_PAGES_PACKAGE = "@working-theory/ui-scheduling";
|
|
25
36
|
function pascalCase(shortName) {
|
|
26
37
|
return shortName
|
|
27
38
|
.split("-")
|
|
@@ -93,27 +104,78 @@ export function renderPaidSections(present) {
|
|
|
93
104
|
}
|
|
94
105
|
return `${lines.join("\n")}\n`;
|
|
95
106
|
}
|
|
107
|
+
const MODULE_FLAGS_HEADER = `// modules.generated.ts — which module pages this site carries.
|
|
108
|
+
//
|
|
109
|
+
// GENERATED by create-ailk from this app's package.json dependencies (#8843).
|
|
110
|
+
// Do not edit: every scaffold, \`create-ailk add-module\` and
|
|
111
|
+
// \`create-ailk update\` rewrites it. It imports nothing, so edge code may
|
|
112
|
+
// read it as well as the locale layout.
|
|
113
|
+
`;
|
|
96
114
|
/**
|
|
97
|
-
*
|
|
98
|
-
* own
|
|
99
|
-
*
|
|
115
|
+
* Render the module-presence flags. Line 1 is `// oss: false` exactly when a
|
|
116
|
+
* flag is on, the registry's own rule: an `on` flag names pages the public
|
|
117
|
+
* tree does not carry.
|
|
100
118
|
*/
|
|
101
|
-
export function
|
|
119
|
+
export function renderModuleFlags(flags) {
|
|
120
|
+
const lines = [];
|
|
121
|
+
if (flags.schedulingPages)
|
|
122
|
+
lines.push("// oss: false");
|
|
123
|
+
lines.push(MODULE_FLAGS_HEADER.trimEnd());
|
|
124
|
+
lines.push("");
|
|
125
|
+
lines.push("/** The scheduling module's booking pages (/meet, /private) exist. */");
|
|
126
|
+
lines.push(`export const SCHEDULING_PAGES: boolean = ${String(flags.schedulingPages)};`);
|
|
127
|
+
return `${lines.join("\n")}\n`;
|
|
128
|
+
}
|
|
129
|
+
/** The web app's `dependencies`, or `undefined` for a scaffold with no web app. */
|
|
130
|
+
function webDependencies(targetDir, writing) {
|
|
102
131
|
const pkgPath = join(targetDir, "apps/web/package.json");
|
|
103
132
|
if (!existsSync(pkgPath) || !existsSync(join(targetDir, "apps/web/lib"))) {
|
|
104
|
-
return
|
|
133
|
+
return undefined;
|
|
105
134
|
}
|
|
106
135
|
let pkg;
|
|
107
136
|
try {
|
|
108
137
|
pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
109
138
|
}
|
|
110
139
|
catch (err) {
|
|
111
|
-
throw new Error(`cannot read ${pkgPath} to write the
|
|
140
|
+
throw new Error(`cannot read ${pkgPath} to write the ${writing}: ${err.message}`, { cause: err });
|
|
112
141
|
}
|
|
113
|
-
|
|
142
|
+
return pkg.dependencies ?? {};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Write the module-presence flags into a scaffold from its own
|
|
146
|
+
* `apps/web/package.json` (#8843 D10) — never copied from a template, so a
|
|
147
|
+
* site without the scheduling module can never carry `true`. Answers whether
|
|
148
|
+
* the file was `added`, `updated` (changed bytes) or `unchanged`, or
|
|
149
|
+
* `undefined` (writing nothing) for a scaffold with no web app.
|
|
150
|
+
*/
|
|
151
|
+
export function writeModuleFlags(targetDir) {
|
|
152
|
+
const deps = webDependencies(targetDir, "module flags");
|
|
153
|
+
if (deps === undefined)
|
|
154
|
+
return undefined;
|
|
155
|
+
const path = join(targetDir, MODULE_FLAGS_PATH);
|
|
156
|
+
const before = existsSync(path) ? readFileSync(path, "utf8") : undefined;
|
|
157
|
+
const next = renderModuleFlags({
|
|
158
|
+
schedulingPages: Object.hasOwn(deps, SCHEDULING_PAGES_PACKAGE),
|
|
159
|
+
});
|
|
160
|
+
if (before === next)
|
|
161
|
+
return "unchanged";
|
|
162
|
+
writeFileSync(path, next);
|
|
163
|
+
return before === undefined ? "added" : "updated";
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Write the registry into a scaffold, deriving presence from the scaffold's
|
|
167
|
+
* own `apps/web/package.json`, and the module-presence flags beside it.
|
|
168
|
+
* Returns `false` (writing nothing) when that file or `apps/web/lib/` is
|
|
169
|
+
* absent — a scaffold with no web app.
|
|
170
|
+
*/
|
|
171
|
+
export function writePaidSections(targetDir) {
|
|
172
|
+
const deps = webDependencies(targetDir, "paid-sections registry");
|
|
173
|
+
if (deps === undefined)
|
|
174
|
+
return false;
|
|
114
175
|
const present = new Set(paidSectionRows()
|
|
115
176
|
.map((row) => row.package)
|
|
116
177
|
.filter((name) => Object.hasOwn(deps, name)));
|
|
117
178
|
writeFileSync(join(targetDir, PAID_SECTIONS_PATH), renderPaidSections(present));
|
|
179
|
+
writeModuleFlags(targetDir);
|
|
118
180
|
return true;
|
|
119
181
|
}
|
package/dist/seed-flows.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export type SeedFlowOutcome = {
|
|
|
26
26
|
ok: true;
|
|
27
27
|
result: string;
|
|
28
28
|
status: string;
|
|
29
|
+
/** #8850 D6 — the flow's copy is the starter's `{brace}` placeholder. */
|
|
30
|
+
placeholder?: true;
|
|
29
31
|
} | {
|
|
30
32
|
slug: string;
|
|
31
33
|
ok: false;
|
|
@@ -33,6 +35,7 @@ export type SeedFlowOutcome = {
|
|
|
33
35
|
} | {
|
|
34
36
|
slug: string;
|
|
35
37
|
ok: "dry-run";
|
|
38
|
+
placeholder?: true;
|
|
36
39
|
};
|
|
37
40
|
export interface SeedFlowsResult {
|
|
38
41
|
/** The hub origin, when one resolved (a dry run may have none). */
|
|
@@ -49,7 +52,7 @@ export interface SeedFlowsResult {
|
|
|
49
52
|
/** The whole report, one line per row — what `seedFlows` printed as it went. */
|
|
50
53
|
export declare function formatSeedFlowsReport(result: SeedFlowsResult): string;
|
|
51
54
|
export declare function seedFlows(opts: SeedFlowsOptions): Promise<SeedFlowsResult>;
|
|
52
|
-
export declare const SPOKE_FLOWS_README = "# flows/\n\nEach file here is one **declared flow**: `flows/<slug>.json` holds one flow\nconfig, exactly as the hub stores it. This repo owns these configs \u2014 the hub\nruns them for this site.\n\n## What a spoke may declare\n\nA spoke key may seed only what this site's own flow pages show:\n`slug`, `defaultLocale`, `steps`, `thankYou` and `flags`. Anything the\nhub acts on outside these pages \u2014 emails, deliverables, pricing, registers,\nlistings \u2014 is refused (`field_not_seedable`). A flow that needs one of those\nis authored in the hub's Admin instead.\n\nThe file name must be the config's `slug` plus `.json`.\n\n## Seeding them into the hub\n\nRun this as part of each rollout, from the site's root, with the spoke key\nyour hub issued set as `AILK_SPOKE_KEY` in the environment (a shell or CI\nsecret \u2014 never a file in this repo):\n\n npx create-ailk seed-flows .\n\nThe hub origin comes from `--hub-url`, else `AILK_API_URL`, else the\n`AILK_API_URL=` line of `apps/web/.env.local`. The key is read from the\nenvironment only. Each flow answers `created`, `updated` or `unchanged`,\nso running it again is safe. `--dry-run` checks the files and sends nothing.\n\nThe hub's operator must first turn seeding on (`AILK_SPOKE_FLOW_SEED=true`\non the hub). Until it is on and this command has run, links to these flows\n(`/flows/<slug>`) reach no flow.\n\n## Who owns what\n\nThe next seed that differs overwrites the hub's copy of a flow's config, so\nedit flows here, not in the Admin. A flow's status (live or draft) is the\nhub's: switching a seeded flow to draft in the Admin keeps it draft through\nevery later seed. Removing a file here does not delete the hub's flow. If\nthe Admin adds a block only it can author (an email, a deliverable, an\noffer), the hub refuses the next seed of that flow (`slug_owned`) rather\nthan delete the block.\n";
|
|
55
|
+
export declare const SPOKE_FLOWS_README = "# flows/\n\nEach file here is one **declared flow**: `flows/<slug>.json` holds one flow\nconfig, exactly as the hub stores it. This repo owns these configs \u2014 the hub\nruns them for this site.\n\n## What a spoke may declare\n\nA spoke key may seed only what this site's own flow pages show:\n`slug`, `defaultLocale`, `steps`, `thankYou` and `flags`. Anything the\nhub acts on outside these pages \u2014 emails, deliverables, pricing, registers,\nlistings \u2014 is refused (`field_not_seedable`). A flow that needs one of those\nis authored in the hub's Admin instead.\n\nThe file name must be the config's `slug` plus `.json`.\n\n## Seeding them into the hub\n\nRun this as part of each rollout, from the site's root, with the spoke key\nyour hub issued set as `AILK_SPOKE_KEY` in the environment (a shell or CI\nsecret \u2014 never a file in this repo):\n\n npx create-ailk seed-flows .\n\nThe hub origin comes from `--hub-url`, else `AILK_API_URL`, else the\n`AILK_API_URL=` line of `apps/web/.env.local`. The key is read from the\nenvironment only. Each flow answers `created`, `updated` or `unchanged`,\nso running it again is safe. `--dry-run` checks the files and sends nothing.\n\nThe hub's operator must first turn seeding on (`AILK_SPOKE_FLOW_SEED=true`\non the hub). Until it is on and this command has run, links to these flows\n(`/flows/<slug>`) reach no flow.\n\n## The starter's placeholder flows\n\nThe `signal-collectors` module ships two declared flows here:\n`flows/diagnostic.json` (an email step, then two questions) and\n`flows/join-waitlist.json` (an email and an optional name). Every visible\nstring in both is a `{brace}` placeholder: replace each one with your own\ncopy. Both go live on the hub with the first seed, so each line this command\nprints for them says they still carry placeholder copy.\n\nA flow is reached at `/flows/<slug>`. To give one its own page pair, run:\n\n npx create-ailk add-route magnet --path /diagnostic --flow diagnostic\n npx create-ailk add-route magnet --path /waitlist --flow join-waitlist\n\nA job application comes with the `careers` surface. Contact, newsletter and\njob application are the hub's own standard magnets: the hub seeds them in\nevery workspace and refuses a spoke's copy of each, so they need no file\nhere. If the hub's Admin already owns a magnet under one of these two slugs,\nthe hub refuses the seed (`slug_owned`); rename the file and its `slug`.\n\n## Who owns what\n\nThe next seed that differs overwrites the hub's copy of a flow's config, so\nedit flows here, not in the Admin. A flow's status (live or draft) is the\nhub's: switching a seeded flow to draft in the Admin keeps it draft through\nevery later seed. Removing a file here does not delete the hub's flow. If\nthe Admin adds a block only it can author (an email, a deliverable, an\noffer), the hub refuses the next seed of that flow (`slug_owned`) rather\nthan delete the block.\n";
|
|
53
56
|
/**
|
|
54
57
|
* Write `SPOKE_FLOWS_README` to `<targetDir>/flows/README.md` unless that
|
|
55
58
|
* file exists. Returns whether it wrote.
|
package/dist/seed-flows.js
CHANGED
|
@@ -49,6 +49,22 @@ export class SeedFlowsRefusedError extends Error {
|
|
|
49
49
|
this.name = "SeedFlowsRefusedError";
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/** A starter placeholder string: one `{…}` brace pair and nothing else. */
|
|
53
|
+
const PLACEHOLDER_STRING = /^\{[^{}]+\}$/;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a flow config carries the starter's placeholder copy (#8850 D6):
|
|
56
|
+
* some string value anywhere in it is a whole `{brace}` placeholder.
|
|
57
|
+
*/
|
|
58
|
+
function isPlaceholderFlow(config) {
|
|
59
|
+
if (typeof config === "string")
|
|
60
|
+
return PLACEHOLDER_STRING.test(config);
|
|
61
|
+
if (Array.isArray(config))
|
|
62
|
+
return config.some(isPlaceholderFlow);
|
|
63
|
+
if (config !== null && typeof config === "object") {
|
|
64
|
+
return Object.values(config).some(isPlaceholderFlow);
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
52
68
|
function readDeclaredFlows(flowsDir) {
|
|
53
69
|
if (!existsSync(flowsDir))
|
|
54
70
|
return [];
|
|
@@ -78,7 +94,12 @@ function readDeclaredFlows(flowsDir) {
|
|
|
78
94
|
if (slug !== expected) {
|
|
79
95
|
throw new SeedFlowsRefusedError(`seed-flows: flows/${file} declares slug "${slug}"; a declared flow's file is named <slug>.json. Nothing was sent.`);
|
|
80
96
|
}
|
|
81
|
-
return {
|
|
97
|
+
return {
|
|
98
|
+
slug,
|
|
99
|
+
file,
|
|
100
|
+
config: config,
|
|
101
|
+
placeholder: isPlaceholderFlow(config),
|
|
102
|
+
};
|
|
82
103
|
});
|
|
83
104
|
}
|
|
84
105
|
// ── The hub origin ──────────────────────────────────────────────────────────
|
|
@@ -158,6 +179,7 @@ async function sendFlow(fetchImpl, origin, key, flow) {
|
|
|
158
179
|
ok: true,
|
|
159
180
|
result,
|
|
160
181
|
status: safeCode(envelope.data?.status),
|
|
182
|
+
...(flow.placeholder && { placeholder: true }),
|
|
161
183
|
};
|
|
162
184
|
}
|
|
163
185
|
return { slug: flow.slug, ok: false, failure: "200 unknown" };
|
|
@@ -175,12 +197,15 @@ function hubLine(hub) {
|
|
|
175
197
|
: `hub: ${hub.origin} (from ${hub.source})`;
|
|
176
198
|
}
|
|
177
199
|
function flowLine(outcome) {
|
|
178
|
-
if (outcome.ok === "dry-run")
|
|
179
|
-
return `${outcome.slug}: would send`;
|
|
180
200
|
if (outcome.ok === false)
|
|
181
201
|
return `${outcome.slug}: failed — ${outcome.failure}`;
|
|
202
|
+
const placeholder = outcome.placeholder === true
|
|
203
|
+
? ` — placeholder copy: replace every {brace} in flows/${outcome.slug}.json`
|
|
204
|
+
: "";
|
|
205
|
+
if (outcome.ok === "dry-run")
|
|
206
|
+
return `${outcome.slug}: would send${placeholder}`;
|
|
182
207
|
const draft = outcome.status === "draft" ? " (status draft — not shown to visitors)" : "";
|
|
183
|
-
return `${outcome.slug}: ${outcome.result}${draft}`;
|
|
208
|
+
return `${outcome.slug}: ${outcome.result}${draft}${placeholder}`;
|
|
184
209
|
}
|
|
185
210
|
/** The whole report, one line per row — what `seedFlows` printed as it went. */
|
|
186
211
|
export function formatSeedFlowsReport(result) {
|
|
@@ -224,6 +249,7 @@ export async function seedFlows(opts) {
|
|
|
224
249
|
const outcomes = flows.map((flow) => ({
|
|
225
250
|
slug: flow.slug,
|
|
226
251
|
ok: "dry-run",
|
|
252
|
+
...(flow.placeholder && { placeholder: true }),
|
|
227
253
|
}));
|
|
228
254
|
onLine(hubLine(hub));
|
|
229
255
|
outcomes.forEach((outcome) => onLine(flowLine(outcome)));
|
|
@@ -282,6 +308,26 @@ The hub's operator must first turn seeding on (\`AILK_SPOKE_FLOW_SEED=true\`
|
|
|
282
308
|
on the hub). Until it is on and this command has run, links to these flows
|
|
283
309
|
(\`/flows/<slug>\`) reach no flow.
|
|
284
310
|
|
|
311
|
+
## The starter's placeholder flows
|
|
312
|
+
|
|
313
|
+
The \`signal-collectors\` module ships two declared flows here:
|
|
314
|
+
\`flows/diagnostic.json\` (an email step, then two questions) and
|
|
315
|
+
\`flows/join-waitlist.json\` (an email and an optional name). Every visible
|
|
316
|
+
string in both is a \`{brace}\` placeholder: replace each one with your own
|
|
317
|
+
copy. Both go live on the hub with the first seed, so each line this command
|
|
318
|
+
prints for them says they still carry placeholder copy.
|
|
319
|
+
|
|
320
|
+
A flow is reached at \`/flows/<slug>\`. To give one its own page pair, run:
|
|
321
|
+
|
|
322
|
+
npx create-ailk add-route magnet --path /diagnostic --flow diagnostic
|
|
323
|
+
npx create-ailk add-route magnet --path /waitlist --flow join-waitlist
|
|
324
|
+
|
|
325
|
+
A job application comes with the \`careers\` surface. Contact, newsletter and
|
|
326
|
+
job application are the hub's own standard magnets: the hub seeds them in
|
|
327
|
+
every workspace and refuses a spoke's copy of each, so they need no file
|
|
328
|
+
here. If the hub's Admin already owns a magnet under one of these two slugs,
|
|
329
|
+
the hub refuses the seed (\`slug_owned\`); rename the file and its \`slug\`.
|
|
330
|
+
|
|
285
331
|
## Who owns what
|
|
286
332
|
|
|
287
333
|
The next seed that differs overwrites the hub's copy of a flow's config, so
|