create-ailk 0.6.1 → 0.7.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/README.md +43 -4
- package/component-catalog.json +146 -21
- package/component-catalog.md +9 -6
- package/dist/add-module-core.d.ts +22 -0
- package/dist/add-module-core.js +50 -0
- package/dist/add-module.d.ts +89 -0
- package/dist/add-module.js +334 -0
- package/dist/category-profile.d.ts +126 -0
- package/dist/category-profile.js +262 -0
- package/dist/cli.js +73 -11
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -1
- package/dist/lib/extract-module-bundle.d.ts +56 -1
- package/dist/lib/extract-module-bundle.js +165 -13
- package/dist/lib/license-key-files.d.ts +15 -0
- package/dist/lib/license-key-files.js +102 -0
- package/dist/lib/pro-callout.d.ts +16 -10
- package/dist/lib/pro-callout.js +26 -3
- package/dist/license-set.d.ts +34 -0
- package/dist/license-set.js +194 -0
- package/dist/module-architecture.d.ts +20 -0
- package/dist/module-architecture.js +77 -19
- package/dist/modules/commerce/0.6.1.tar.gz +0 -0
- package/dist/modules/docs-blog/0.6.1.tar.gz +0 -0
- package/dist/modules/marketing-site/0.6.1.tar.gz +0 -0
- package/dist/modules/minimalism-site/0.6.1.tar.gz +0 -0
- package/dist/modules/multi-tenant/0.6.1.tar.gz +0 -0
- package/dist/modules/saas-product/0.6.1.tar.gz +0 -0
- package/dist/modules/scheduling/0.6.1.tar.gz +0 -0
- package/dist/modules/signal-collectors/0.6.1.tar.gz +0 -0
- package/dist/modules/simple-service/0.6.1.tar.gz +0 -0
- package/dist/modules/workspace/0.6.1.tar.gz +0 -0
- package/dist/parse-args.d.ts +7 -0
- package/dist/parse-args.js +23 -1
- package/dist/programmatic.d.ts +46 -0
- package/dist/programmatic.js +130 -9
- package/dist/site-chrome.d.ts +18 -7
- package/dist/site-chrome.js +32 -14
- package/dist/site-roster.d.ts +127 -0
- package/dist/site-roster.js +494 -0
- package/dist/surfaces.d.ts +20 -7
- package/dist/surfaces.js +149 -28
- package/dist/sync-routes.d.ts +9 -0
- package/dist/sync-routes.js +2 -2
- package/dist/template-manifest.d.ts +58 -2
- package/dist/template-manifest.js +196 -69
- package/package.json +6 -4
- package/site-templates/_base/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +66 -0
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +68 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +71 -62
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +2 -3
- package/site-templates/_base/content/en/TermsPage/terms.mdx +2 -3
- package/site-templates/_base/content/en/UseCase/first-use-case.mdx +23 -0
- package/site-templates/_base/content/en/UseCase/second-use-case.mdx +23 -0
- package/site-templates/_base/content/en/UseCase/third-use-case.mdx +23 -0
- package/site-templates/designs/minimalism/DESIGN.md +162 -0
- package/site-templates/designs/standard/DESIGN.md +162 -0
- package/site-templates/marketing-site/DESIGN.md +55 -41
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +312 -57
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +209 -90
- package/site-templates/marketing-site/ailk-template.json +1 -0
- package/site-templates/marketing-site-free/DESIGN.md +55 -41
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +260 -66
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +294 -114
- package/site-templates/marketing-site-free/ailk-template.json +3 -2
- package/site-templates/minimalism-site/DESIGN.md +54 -40
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +246 -19
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +185 -51
- package/site-templates/minimalism-site/ailk-template.json +3 -2
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +3 -37
- package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
- package/site-templates/saas-product/DESIGN.md +55 -41
- package/site-templates/simple-service/DESIGN.md +55 -41
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +135 -26
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +114 -59
- package/site-templates/simple-service/ailk-template.json +4 -3
- package/site-templates/simple-service/content/en/BookCall/book-call.mdx +53 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +1 -36
- package/site-templates/simple-service/content/en/PurchasePolicyPage/purchase-policy.mdx +124 -0
- package/templates/.claude/settings.json +15 -0
- package/templates/.claude/skills/README.md +8 -8
- package/templates/.claude/skills/{scaffold-auth → add-auth}/SKILL.md +7 -7
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/env-vars.md +2 -2
- package/templates/.claude/skills/{scaffold-auth → add-auth}/references/oauth-app-setup.md +3 -3
- package/templates/.claude/skills/{scaffold-page → add-page}/SKILL.md +76 -62
- package/templates/.claude/skills/add-page/references/page-types.md +111 -0
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/content.mdx.tmpl +4 -4
- package/templates/.claude/skills/{scaffold-page → add-page}/templates/route.tsx.tmpl +1 -1
- package/templates/.claude/skills/{audit-site → check-site}/SKILL.md +16 -17
- package/templates/.claude/skills/{audit-site → check-site}/references/common-errors.md +3 -3
- package/templates/.claude/skills/{audit-site → check-site}/references/findings-glossary.md +16 -14
- package/templates/.claude/skills/{audit-site → check-site}/references/severity-levels.md +1 -1
- package/templates/.claude/skills/fill-page/SKILL.md +180 -0
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +80 -0
- package/templates/.claude/skills/fill-page/references/section-sources.md +41 -0
- package/templates/.claude/skills/{provision-config → setup-deploy}/SKILL.md +8 -8
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/env-vars-template.md +2 -2
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/runbook-template.md +8 -8
- package/templates/.claude/skills/{provision-config → setup-deploy}/references/vercel-json-template.md +1 -1
- package/templates/.claude/skills/upgrade-plan/SKILL.md +117 -0
- package/templates/.claude/skills/upgrade-plan/references/options.md +24 -0
- package/templates/.claude/skills/upgrade-plan/scripts/upgrade-plan.ts +527 -0
- package/templates/.github/workflows/ci.yml +5 -0
- package/templates/CLAUDE.md +8 -2
- package/templates/DESIGN.md +52 -45
- package/templates/README.md +108 -46
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/package.json +1 -1
- package/templates/apps/api/src/config/__tests__/index.test.ts +37 -2
- package/templates/apps/api/src/config/index.ts +19 -6
- package/templates/apps/api/src/lib/license-key-store.ts +45 -0
- package/templates/apps/api/src/lib/site.ts +5 -3
- package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +97 -0
- package/templates/apps/api/src/middleware/auth.ts +9 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +127 -5
- package/templates/apps/api/src/routes/content/get-page.ts +86 -2
- package/templates/apps/api/src/server.ts +6 -0
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +75 -1
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +57 -9
- package/templates/apps/mcp/package.json +2 -1
- package/templates/apps/mcp/src/index.ts +7 -0
- package/templates/apps/mcp/src/lib/audit-single-page.ts +36 -10
- package/templates/apps/mcp/src/lib/license-key-store.ts +45 -0
- package/templates/apps/web/__tests__/setup.ts +18 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +10 -2
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +2 -2
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +10 -4
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +22 -14
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +26 -18
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +12 -4
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +35 -4
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +49 -9
- package/templates/apps/web/app/[locale]/book-a-call/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +94 -33
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +53 -23
- package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +84 -2
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +5 -6
- package/templates/apps/web/app/[locale]/contact/page.tsx +25 -7
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/glossary/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/how-to/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/layout.tsx +113 -51
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +50 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/{shipping → purchase-policy}/page.tsx +15 -10
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +4 -5
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/software/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/page.tsx +25 -15
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +48 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +3 -4
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +15 -2
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/whitepapers/page.tsx +25 -15
- package/templates/apps/web/app/error-fallback.tsx +17 -45
- package/templates/apps/web/app/not-found.tsx +14 -38
- package/templates/apps/web/app/og/route.tsx +6 -0
- package/templates/apps/web/eslint.config.mjs +3 -0
- package/templates/apps/web/instrumentation.ts +14 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +38 -0
- package/templates/apps/web/lib/license-key-store.ts +52 -0
- package/templates/apps/web/lib/nav-config.ts +64 -12
- package/templates/apps/web/lib/prisma-for-auth.ts +9 -6
- package/templates/apps/web/lib/site-brand.tsx +1 -1
- package/templates/apps/web/lib/site-chrome.ts +3 -3
- package/templates/apps/web/lib/site-identity.ts +134 -0
- package/templates/apps/web/lib/site-theme.ts +11 -0
- package/templates/apps/web/messages/en.json +52 -37
- package/templates/apps/web/public/android-chrome-192x192.png +0 -0
- package/templates/apps/web/public/android-chrome-512x512.png +0 -0
- package/templates/apps/web/public/apple-touch-icon.png +0 -0
- package/templates/apps/web/public/favicon-16x16.png +0 -0
- package/templates/apps/web/public/favicon-32x32.png +0 -0
- package/templates/apps/web/public/favicon.ico +0 -0
- package/templates/apps/web/public/favicon.svg +3 -3
- package/templates/apps/web/public/logomark.svg +6 -3
- package/templates/apps/web/public/logos/ai-launch-kit-light.svg +4 -0
- package/templates/apps/web/public/logos/ai-launch-kit.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent-light.svg +4 -0
- package/templates/apps/web/public/logos/atomic-agent.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth-light.svg +4 -0
- package/templates/apps/web/public/logos/salessmyth.svg +4 -0
- package/templates/apps/web/public/site.webmanifest +4 -4
- package/templates/apps/web/public/wordmark-light.svg +4 -0
- package/templates/apps/web/public/wordmark.svg +3 -3
- package/templates/apps/web/security-headers.config.ts +5 -1
- package/templates/content/_site.mdx +23 -0
- package/templates/content/en/AboutPage/about.mdx +150 -144
- package/templates/content/en/BlogList/blog.mdx +105 -0
- package/templates/content/en/ContactPage/contact.mdx +44 -85
- package/templates/content/en/FAQPage/faq.mdx +86 -57
- package/templates/content/en/HomePage/home.mdx +276 -217
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +182 -0
- package/templates/content/en/SoftwareProduct/product.mdx +175 -0
- package/templates/content/en/UseCasesList/use-cases.mdx +170 -0
- package/templates/database/CHANGELOG.md +7 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +3 -1
- package/templates/tools/lint-architecture.ts +315 -0
- package/templates/tools/tsconfig.json +14 -0
- package/versions.json +33 -33
- package/templates/.claude/skills/add-schema/SKILL.md +0 -277
- package/templates/.claude/skills/add-schema/references/conflict-handling.md +0 -200
- package/templates/.claude/skills/add-schema/references/file-finding.md +0 -123
- package/templates/.claude/skills/add-schema/references/schema-types.md +0 -234
- package/templates/.claude/skills/scaffold-page/references/page-types.md +0 -169
- package/templates/.claude/skills/suggest-site-pages/SKILL.md +0 -242
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +0 -287
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +0 -44
- package/templates/apps/web/app/[locale]/returns/page.tsx +0 -44
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +0 -108
- package/templates/content/en/LandingPage/blog.mdx +0 -83
- package/templates/content/en/LandingPage/features.mdx +0 -103
- package/templates/content/en/LandingPage/how-it-works.mdx +0 -143
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +0 -108
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +0 -108
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The contract's shape table, as data: each shape's primary node, the
|
|
3
|
+
* secondaries it allows, and whether it is reserved. Keyed by the manifest's
|
|
4
|
+
* own `StarterShape`, so a shape added there without a row here fails
|
|
5
|
+
* type-check.
|
|
6
|
+
*/
|
|
7
|
+
export const CATEGORY_PROFILE_SHAPES = {
|
|
8
|
+
"organization-product": {
|
|
9
|
+
primary: "Organization",
|
|
10
|
+
secondaries: ["SoftwareApplication"],
|
|
11
|
+
reserved: false,
|
|
12
|
+
},
|
|
13
|
+
"organization-service": {
|
|
14
|
+
primary: "Organization",
|
|
15
|
+
secondaries: [],
|
|
16
|
+
reserved: false,
|
|
17
|
+
},
|
|
18
|
+
"local-business-service": {
|
|
19
|
+
primary: "LocalBusiness",
|
|
20
|
+
secondaries: ["Person"],
|
|
21
|
+
reserved: false,
|
|
22
|
+
},
|
|
23
|
+
"local-business-product": {
|
|
24
|
+
primary: "LocalBusiness",
|
|
25
|
+
secondaries: [],
|
|
26
|
+
reserved: true,
|
|
27
|
+
},
|
|
28
|
+
"person-service": {
|
|
29
|
+
primary: "Person",
|
|
30
|
+
secondaries: ["Organization"],
|
|
31
|
+
reserved: false,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* schema.org's `LocalBusiness` subtree, `LocalBusiness` itself included — the
|
|
36
|
+
* closure of the `LocalBusiness` union in `schema-dts`. A literal so `src/`
|
|
37
|
+
* needs no type package at run time; `category-profile.test.ts` proves it
|
|
38
|
+
* set-equal to the `schema-dts` closure, so it cannot drift silently.
|
|
39
|
+
*/
|
|
40
|
+
export const LOCAL_BUSINESS_TYPES = new Set([
|
|
41
|
+
"AccountingService", "AdultEntertainment", "AmusementPark", "AnimalShelter",
|
|
42
|
+
"ArchiveOrganization", "ArtGallery", "Attorney", "AutoBodyShop", "AutoDealer",
|
|
43
|
+
"AutoPartsStore", "AutoRental", "AutoRepair", "AutoWash", "AutomatedTeller",
|
|
44
|
+
"AutomotiveBusiness", "Bakery", "BankOrCreditUnion", "BarOrPub", "BeautySalon",
|
|
45
|
+
"BedAndBreakfast", "BikeStore", "BookStore", "BowlingAlley", "Brewery",
|
|
46
|
+
"CafeOrCoffeeShop", "Campground", "Casino", "ChildCare", "ClothingStore",
|
|
47
|
+
"ComedyClub", "CommunityHealth", "ComputerStore", "ConvenienceStore",
|
|
48
|
+
"CovidTestingFacility", "DaySpa", "Dentist", "DepartmentStore", "Dermatology",
|
|
49
|
+
"DietNutrition", "Distillery", "DryCleaningOrLaundry", "Electrician",
|
|
50
|
+
"ElectronicsStore", "Emergency", "EmergencyService", "EmploymentAgency",
|
|
51
|
+
"EntertainmentBusiness", "ExerciseGym", "FastFoodRestaurant", "FinancialService",
|
|
52
|
+
"FireStation", "Florist", "FoodEstablishment", "FurnitureStore", "GardenStore",
|
|
53
|
+
"GasStation", "GeneralContractor", "Geriatric", "GolfCourse", "GovernmentOffice",
|
|
54
|
+
"GroceryStore", "Gynecologic", "HVACBusiness", "HairSalon", "HardwareStore",
|
|
55
|
+
"HealthAndBeautyBusiness", "HealthClub", "HobbyShop", "HomeAndConstructionBusiness",
|
|
56
|
+
"HomeGoodsStore", "Hospital", "Hostel", "Hotel", "HousePainter", "IceCreamShop",
|
|
57
|
+
"IndividualPhysician", "InsuranceAgency", "InternetCafe", "JewelryStore",
|
|
58
|
+
"LegalService", "Library", "LiquorStore", "LocalBusiness", "Locksmith",
|
|
59
|
+
"LodgingBusiness", "MedicalBusiness", "MedicalClinic", "MensClothingStore",
|
|
60
|
+
"Midwifery", "MobilePhoneStore", "Motel", "MotorcycleDealer", "MotorcycleRepair",
|
|
61
|
+
"MovieRentalStore", "MovieTheater", "MovingCompany", "MusicStore", "NailSalon",
|
|
62
|
+
"NightClub", "Notary", "Nursing", "Obstetric", "OfficeEquipmentStore", "Oncologic",
|
|
63
|
+
"Optician", "Optometric", "Otolaryngologic", "OutletStore", "PawnShop", "Pediatric",
|
|
64
|
+
"PetStore", "Pharmacy", "Physician", "PhysiciansOffice", "Physiotherapy",
|
|
65
|
+
"PlasticSurgery", "Plumber", "Podiatric", "PoliceStation", "PostOffice",
|
|
66
|
+
"PrimaryCare", "ProfessionalService", "Psychiatric", "PublicHealth",
|
|
67
|
+
"PublicSwimmingPool", "RadioStation", "RealEstateAgent", "RecyclingCenter",
|
|
68
|
+
"Resort", "Restaurant", "RoofingContractor", "SelfStorage", "ShoeStore",
|
|
69
|
+
"ShoppingCenter", "SkiResort", "SportingGoodsStore", "SportsActivityLocation",
|
|
70
|
+
"SportsClub", "StadiumOrArena", "Store", "TattooParlor", "TelevisionStation",
|
|
71
|
+
"TennisComplex", "TireShop", "TouristInformationCenter", "ToyStore",
|
|
72
|
+
"TravelAgency", "VacationRental", "WholesaleStore", "Winery",
|
|
73
|
+
]);
|
|
74
|
+
export const OFFERING_PRICING = ["quote", "fixed", "hourly", "subscription"];
|
|
75
|
+
export const PROOF_KINDS = [
|
|
76
|
+
"credential",
|
|
77
|
+
"reviews",
|
|
78
|
+
"before-after",
|
|
79
|
+
"response-time",
|
|
80
|
+
"guarantee",
|
|
81
|
+
"experience",
|
|
82
|
+
];
|
|
83
|
+
export const CONTENT_POSTURES = ["instructions", "sample"];
|
|
84
|
+
/** Thrown on an invalid profile or intake; the message names the failing field path. */
|
|
85
|
+
export class CategoryProfileError extends Error {
|
|
86
|
+
constructor(message) {
|
|
87
|
+
super(message);
|
|
88
|
+
this.name = "CategoryProfileError";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function fail(path, message) {
|
|
92
|
+
throw new CategoryProfileError(`${path}: ${message}`);
|
|
93
|
+
}
|
|
94
|
+
function object(value, path) {
|
|
95
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
96
|
+
fail(path, "must be a mapping");
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function keys(value, path, required, optional = []) {
|
|
101
|
+
const allowed = new Set([...required, ...optional]);
|
|
102
|
+
for (const key of Object.keys(value)) {
|
|
103
|
+
if (!allowed.has(key))
|
|
104
|
+
fail(path === "" ? key : `${path}.${key}`, "unknown key");
|
|
105
|
+
}
|
|
106
|
+
for (const key of required) {
|
|
107
|
+
if (!(key in value))
|
|
108
|
+
fail(path === "" ? key : `${path}.${key}`, "is required");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function string(value, path) {
|
|
112
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
113
|
+
fail(path, "must be a non-empty string");
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
function oneOf(value, allowed, path) {
|
|
117
|
+
if (!allowed.includes(value)) {
|
|
118
|
+
fail(path, `must be one of ${allowed.join(", ")} (got ${JSON.stringify(value)})`);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
function list(value, path, nonEmpty) {
|
|
123
|
+
if (!Array.isArray(value))
|
|
124
|
+
fail(path, "must be a list");
|
|
125
|
+
if (nonEmpty && value.length === 0)
|
|
126
|
+
fail(path, "must not be empty");
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function strings(value, path) {
|
|
130
|
+
return list(value, path, true).map((item, i) => string(item, `${path}[${i}]`));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validate a parsed category profile.
|
|
134
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
135
|
+
*/
|
|
136
|
+
export function parseCategoryProfile(raw, options) {
|
|
137
|
+
const doc = object(raw, "(profile)");
|
|
138
|
+
keys(doc, "", ["slug", "shape", "offerings", "proof", "vocabulary", "roster", "nav", "content_posture", "intake"], ["subtype", "secondary"]);
|
|
139
|
+
const slug = string(doc.slug, "slug");
|
|
140
|
+
if (!options.categories.has(slug))
|
|
141
|
+
fail("slug", `"${slug}" is not a leaf category slug`);
|
|
142
|
+
if (slug !== options.fileSlug) {
|
|
143
|
+
fail("slug", `"${slug}" does not match its file name "${options.fileSlug}.yaml"`);
|
|
144
|
+
}
|
|
145
|
+
const shapes = Object.keys(CATEGORY_PROFILE_SHAPES);
|
|
146
|
+
const shape = oneOf(doc.shape, shapes, "shape");
|
|
147
|
+
const shapeRow = CATEGORY_PROFILE_SHAPES[shape];
|
|
148
|
+
if (shapeRow.reserved)
|
|
149
|
+
fail("shape", `"${shape}" is reserved and not yet built`);
|
|
150
|
+
let subtype;
|
|
151
|
+
if (shapeRow.primary === "LocalBusiness") {
|
|
152
|
+
subtype = string(doc.subtype, "subtype");
|
|
153
|
+
if (!LOCAL_BUSINESS_TYPES.has(subtype)) {
|
|
154
|
+
fail("subtype", `"${subtype}" is not a schema.org LocalBusiness subtype`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (doc.subtype !== undefined) {
|
|
158
|
+
fail("subtype", `is only valid on a local-business-* shape, not "${shape}"`);
|
|
159
|
+
}
|
|
160
|
+
let secondary;
|
|
161
|
+
if (doc.secondary !== undefined) {
|
|
162
|
+
if (shapeRow.secondaries.length === 0)
|
|
163
|
+
fail("secondary", `is not allowed on "${shape}"`);
|
|
164
|
+
secondary = oneOf(doc.secondary, shapeRow.secondaries, "secondary");
|
|
165
|
+
}
|
|
166
|
+
const offerings = list(doc.offerings, "offerings", true).map((item, i) => {
|
|
167
|
+
const path = `offerings[${i}]`;
|
|
168
|
+
const o = object(item, path);
|
|
169
|
+
keys(o, path, ["name", "job", "pricing"]);
|
|
170
|
+
return {
|
|
171
|
+
name: string(o.name, `${path}.name`),
|
|
172
|
+
job: string(o.job, `${path}.job`),
|
|
173
|
+
pricing: oneOf(o.pricing, OFFERING_PRICING, `${path}.pricing`),
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
const proof = list(doc.proof, "proof", true).map((item, i) => {
|
|
177
|
+
const path = `proof[${i}]`;
|
|
178
|
+
const p = object(item, path);
|
|
179
|
+
keys(p, path, ["kind", "label"]);
|
|
180
|
+
return { kind: oneOf(p.kind, PROOF_KINDS, `${path}.kind`), label: string(p.label, `${path}.label`) };
|
|
181
|
+
});
|
|
182
|
+
const vocab = object(doc.vocabulary, "vocabulary");
|
|
183
|
+
keys(vocab, "vocabulary", ["problem", "service", "outcome"]);
|
|
184
|
+
const vocabulary = {
|
|
185
|
+
problem: strings(vocab.problem, "vocabulary.problem"),
|
|
186
|
+
service: strings(vocab.service, "vocabulary.service"),
|
|
187
|
+
outcome: strings(vocab.outcome, "vocabulary.outcome"),
|
|
188
|
+
};
|
|
189
|
+
const roster = list(doc.roster ?? [], "roster", false).map((item, i) => {
|
|
190
|
+
const path = `roster[${i}]`;
|
|
191
|
+
const r = object(item, path);
|
|
192
|
+
const op = oneOf(r.op, ["add", "drop"], `${path}.op`);
|
|
193
|
+
const route = string(r.route, `${path}.route`);
|
|
194
|
+
if (!route.startsWith("/"))
|
|
195
|
+
fail(`${path}.route`, `must start with "/"`);
|
|
196
|
+
if (op === "drop") {
|
|
197
|
+
keys(r, path, ["op", "route", "reason"]);
|
|
198
|
+
return { op, route, reason: string(r.reason, `${path}.reason`) };
|
|
199
|
+
}
|
|
200
|
+
keys(r, path, ["op", "route", "pageType", "reason"]);
|
|
201
|
+
const pageType = string(r.pageType, `${path}.pageType`);
|
|
202
|
+
if (!options.pageTypes.has(pageType)) {
|
|
203
|
+
fail(`${path}.pageType`, `"${pageType}" is not a registered page type`);
|
|
204
|
+
}
|
|
205
|
+
return { op, route, pageType, reason: string(r.reason, `${path}.reason`) };
|
|
206
|
+
});
|
|
207
|
+
const navDoc = doc.nav === null ? {} : object(doc.nav, "nav");
|
|
208
|
+
const nav = Object.fromEntries(Object.entries(navDoc).map(([from, to]) => [from, string(to, `nav.${from}`)]));
|
|
209
|
+
const ids = new Set();
|
|
210
|
+
const intake = list(doc.intake, "intake", true).map((item, i) => {
|
|
211
|
+
const path = `intake[${i}]`;
|
|
212
|
+
const q = object(item, path);
|
|
213
|
+
keys(q, path, ["id", "ask", "fills"]);
|
|
214
|
+
const id = string(q.id, `${path}.id`);
|
|
215
|
+
if (ids.has(id))
|
|
216
|
+
fail(`${path}.id`, `"${id}" is not unique`);
|
|
217
|
+
ids.add(id);
|
|
218
|
+
return { id, ask: string(q.ask, `${path}.ask`), fills: strings(q.fills, `${path}.fills`) };
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
slug,
|
|
222
|
+
shape,
|
|
223
|
+
...(subtype !== undefined ? { subtype } : {}),
|
|
224
|
+
...(secondary !== undefined ? { secondary } : {}),
|
|
225
|
+
offerings,
|
|
226
|
+
proof,
|
|
227
|
+
vocabulary,
|
|
228
|
+
roster,
|
|
229
|
+
nav,
|
|
230
|
+
content_posture: oneOf(doc.content_posture, CONTENT_POSTURES, "content_posture"),
|
|
231
|
+
intake,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Validate a recorded intake (`content/_intake.yaml`) against its profile.
|
|
236
|
+
* Unknown ids fail; an id the file omits is returned as `null` (unanswered).
|
|
237
|
+
* @throws CategoryProfileError naming the failing field path.
|
|
238
|
+
*/
|
|
239
|
+
export function parseIntake(raw, profile) {
|
|
240
|
+
const doc = object(raw, "(intake)");
|
|
241
|
+
keys(doc, "", ["answers"]);
|
|
242
|
+
const given = doc.answers === null ? {} : object(doc.answers, "answers");
|
|
243
|
+
const known = new Set(profile.intake.map((q) => q.id));
|
|
244
|
+
for (const id of Object.keys(given)) {
|
|
245
|
+
if (!known.has(id))
|
|
246
|
+
fail(`answers.${id}`, "is not an intake id of this profile");
|
|
247
|
+
}
|
|
248
|
+
const answers = {};
|
|
249
|
+
for (const { id } of profile.intake) {
|
|
250
|
+
const value = given[id];
|
|
251
|
+
const path = `answers.${id}`;
|
|
252
|
+
if (value === undefined || value === null)
|
|
253
|
+
answers[id] = null;
|
|
254
|
+
else if (typeof value === "string")
|
|
255
|
+
answers[id] = value.trim() === "" ? null : value;
|
|
256
|
+
else if (Array.isArray(value))
|
|
257
|
+
answers[id] = value.map((v, i) => string(v, `${path}[${i}]`));
|
|
258
|
+
else
|
|
259
|
+
fail(path, "must be a string, a list of strings, or null");
|
|
260
|
+
}
|
|
261
|
+
return { answers };
|
|
262
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
* `create-ailk` — entry point.
|
|
4
4
|
*
|
|
5
5
|
* Orchestration:
|
|
6
|
-
* 0. on
|
|
6
|
+
* 0. on an `add-module` / `upgrade` subcommand: parse its own argv, run
|
|
7
|
+
* addModule(), print the report, exit (2 on a refusal it names)
|
|
8
|
+
* on a `sync-routes` subcommand: parse its own argv, run syncRoutesSync(),
|
|
7
9
|
* print the report, exit (1 iff routes are missing in check mode)
|
|
10
|
+
* on a `license set` subcommand: write the key into the scaffold, push it
|
|
11
|
+
* to a linked Vercel project and redeploy, exit (#6683 D9)
|
|
8
12
|
* 1. parse argv → CliOptions
|
|
9
13
|
* 2. on --help: print helpText, exit 0
|
|
10
14
|
* 3. on --version: print version, exit 0
|
|
@@ -30,9 +34,11 @@ import { spawnSync } from "node:child_process";
|
|
|
30
34
|
import { readFileSync } from "node:fs";
|
|
31
35
|
import { dirname, resolve } from "node:path";
|
|
32
36
|
import { fileURLToPath } from "node:url";
|
|
33
|
-
import {
|
|
37
|
+
import { ADD_MODULE_HELP, AddModuleRefusedError, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
|
|
38
|
+
import { FREE_PREREQUISITES_NOTE, LICENSED_PREREQUISITES_NOTE, isLicensedInstall, proCallout, } from "./lib/pro-callout.js";
|
|
39
|
+
import { LICENSE_SET_HELP, runLicenseSet } from "./license-set.js";
|
|
34
40
|
import { helpText, parseCliArgs, parseModules } from "./parse-args.js";
|
|
35
|
-
import { formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
41
|
+
import { addModule, formatSurfaceDrift, scaffold, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
36
42
|
import { CORE_SURFACE_PATHS, SURFACE_ALIASES } from "./surfaces.js";
|
|
37
43
|
import { SYNC_ROUTES_HELP, formatSyncRoutesReport, parseSyncRoutesArgs, } from "./sync-routes.js";
|
|
38
44
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -83,6 +89,32 @@ function readScaffoldPrerequisites(targetAbs) {
|
|
|
83
89
|
}
|
|
84
90
|
async function main() {
|
|
85
91
|
const argv = process.argv.slice(2);
|
|
92
|
+
// `add-module` (alias `upgrade`, #6653): apply a purchased module to an
|
|
93
|
+
// existing site. Its own argv shape, like `sync-routes` below. A refusal it
|
|
94
|
+
// names (a free module, a patch outside the config allowlist, an unreadable
|
|
95
|
+
// architecture.yaml) exits 2; anything else — the licence gate included —
|
|
96
|
+
// falls through to the top-level catch, exit 1, as a scaffold would.
|
|
97
|
+
if (argv[0] === "add-module" || argv[0] === "upgrade") {
|
|
98
|
+
const args = parseAddModuleArgs(argv.slice(1));
|
|
99
|
+
if (args.help || args.module === undefined) {
|
|
100
|
+
process.stdout.write(ADD_MODULE_HELP);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const result = await addModule({
|
|
105
|
+
targetDir: args.target ?? process.cwd(),
|
|
106
|
+
moduleName: args.module,
|
|
107
|
+
});
|
|
108
|
+
process.stdout.write(formatAddModuleReport(result, args.module));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (!(err instanceof AddModuleRefusedError))
|
|
112
|
+
throw err;
|
|
113
|
+
process.stderr.write(`[create-ailk] ${err.message}\n`);
|
|
114
|
+
process.exit(err.exitCode);
|
|
115
|
+
}
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
86
118
|
// Subcommand dispatch, ahead of the flag parser: `sync-routes` has its own
|
|
87
119
|
// argv shape (#4986). Check mode exits 1 iff a template route is missing;
|
|
88
120
|
// locally modified routes are reported but do not fail; --apply exits 0.
|
|
@@ -99,6 +131,16 @@ async function main() {
|
|
|
99
131
|
process.stdout.write(formatSyncRoutesReport(result, args.apply));
|
|
100
132
|
process.exit(!args.apply && result.missing.length > 0 ? 1 : 0);
|
|
101
133
|
}
|
|
134
|
+
// `license set <key|-> [dir]` (#6683 D9): its own argv shape, ahead of the
|
|
135
|
+
// flag parser like `sync-routes`. The key reaches `vercel` on stdin only.
|
|
136
|
+
if (argv[0] === "license" && argv[1] === "set") {
|
|
137
|
+
process.exit(runLicenseSet(argv.slice(2), {
|
|
138
|
+
cwd: process.cwd(),
|
|
139
|
+
stdout: (text) => process.stdout.write(text),
|
|
140
|
+
stderr: (text) => process.stderr.write(text),
|
|
141
|
+
readStdin: () => readFileSync(0, "utf8"),
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
102
144
|
const opts = parseCliArgs(argv);
|
|
103
145
|
// Deprecation notice for a retired `--surfaces` id (#5563 D2). `surfaces.ts`
|
|
104
146
|
// stays pure — `canonicalizeSurface` only resolves the alias — so this is
|
|
@@ -118,7 +160,13 @@ async function main() {
|
|
|
118
160
|
}
|
|
119
161
|
}
|
|
120
162
|
if (opts.help) {
|
|
121
|
-
process.stdout.write(helpText +
|
|
163
|
+
process.stdout.write(helpText +
|
|
164
|
+
"\n" +
|
|
165
|
+
SYNC_ROUTES_HELP +
|
|
166
|
+
"\n" +
|
|
167
|
+
ADD_MODULE_HELP +
|
|
168
|
+
"\n" +
|
|
169
|
+
LICENSE_SET_HELP);
|
|
122
170
|
process.exit(0);
|
|
123
171
|
}
|
|
124
172
|
if (opts.version) {
|
|
@@ -176,6 +224,7 @@ async function main() {
|
|
|
176
224
|
// hands `scaffold()` exactly the option object it got before the flag
|
|
177
225
|
// existed (spec 5481 § 3 DD3's byte-identity floor).
|
|
178
226
|
...(opts.template !== undefined ? { template: opts.template } : {}),
|
|
227
|
+
...(opts.license !== undefined ? { license: opts.license } : {}),
|
|
179
228
|
});
|
|
180
229
|
if (opts.install) {
|
|
181
230
|
// Hard-coded argv. User input only flows through `cwd`, never the command.
|
|
@@ -188,7 +237,12 @@ async function main() {
|
|
|
188
237
|
throw new Error(`pnpm install failed in ${targetAbs} (exit ${result.status}). Run it manually after fixing the issue.`);
|
|
189
238
|
}
|
|
190
239
|
}
|
|
191
|
-
// Next-steps message.
|
|
240
|
+
// Next-steps message. A licensed install (#6480) is not told it needs no
|
|
241
|
+
// licence key, and is not pitched the Pro tier it already owns.
|
|
242
|
+
const licensed = isLicensedInstall({
|
|
243
|
+
...(opts.template !== undefined ? { template: opts.template } : {}),
|
|
244
|
+
modules: opts.modules,
|
|
245
|
+
});
|
|
192
246
|
const lines = [
|
|
193
247
|
"",
|
|
194
248
|
opts.template !== undefined
|
|
@@ -199,8 +253,9 @@ async function main() {
|
|
|
199
253
|
// Prerequisites (#6440 AC4) — every one the scaffold enforces, and nothing
|
|
200
254
|
// it does not. A starter reaches `pnpm dev` AND `pnpm build` on a toolchain
|
|
201
255
|
// alone: the Prisma client is generated by `database`'s own `postinstall`,
|
|
202
|
-
// so there is no generator step to remember, and no database
|
|
203
|
-
//
|
|
256
|
+
// so there is no generator step to remember, and no database or vendor API
|
|
257
|
+
// key gates either command (a free install also says no licence key does;
|
|
258
|
+
// a licensed one, #6480, does not). Saying so is the point — the previous
|
|
204
259
|
// output named no prerequisites at all, which left a customer to discover
|
|
205
260
|
// the toolchain floor from a failure.
|
|
206
261
|
const prereqs = readScaffoldPrerequisites(targetAbs);
|
|
@@ -210,7 +265,12 @@ async function main() {
|
|
|
210
265
|
].filter((part) => part !== undefined);
|
|
211
266
|
if (toolchain.length > 0) {
|
|
212
267
|
lines.push(`Prerequisites: ${toolchain.join(" and ")} — that is all.`);
|
|
213
|
-
lines.push(
|
|
268
|
+
lines.push(licensed ? LICENSED_PREREQUISITES_NOTE : FREE_PREREQUISITES_NOTE);
|
|
269
|
+
lines.push("");
|
|
270
|
+
}
|
|
271
|
+
// Only when asked for (#6683 D9) — a run without --license is unchanged.
|
|
272
|
+
if (opts.license !== undefined) {
|
|
273
|
+
lines.push("Licence key written to apps/api/.env and ailk.key (owner-only, gitignored).");
|
|
214
274
|
lines.push("");
|
|
215
275
|
}
|
|
216
276
|
lines.push("Next steps:");
|
|
@@ -226,15 +286,17 @@ async function main() {
|
|
|
226
286
|
lines.push("");
|
|
227
287
|
lines.push(`Route surfaces: ${CORE_SURFACE_PATHS.join(", ")}` +
|
|
228
288
|
(surfaces.length > 0 ? ` + ${surfaces.join(", ")}` : ""));
|
|
229
|
-
lines.push("Add another with the `
|
|
289
|
+
lines.push("Add another with the `add-page` skill, or re-run with --surfaces <id>.");
|
|
230
290
|
// Only when asked for. The default scaffold's output is unchanged.
|
|
231
291
|
if (opts.withCatalog) {
|
|
232
292
|
lines.push("Component catalog: COMPONENT-CATALOG.md — search it by capability before building a component.");
|
|
233
293
|
}
|
|
234
294
|
lines.push("Reference: ADR 0007 — pnpm create ailk@latest is the canonical install path.");
|
|
235
295
|
lines.push("");
|
|
236
|
-
|
|
237
|
-
|
|
296
|
+
if (!licensed) {
|
|
297
|
+
lines.push(proCallout());
|
|
298
|
+
lines.push("");
|
|
299
|
+
}
|
|
238
300
|
process.stdout.write(lines.join("\n"));
|
|
239
301
|
}
|
|
240
302
|
main().catch((err) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { copyTemplate, type CopyTemplateOptions } from "./copy.js";
|
|
7
7
|
export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, type ModuleName, type CliOptions, } from "./parse-args.js";
|
|
8
|
-
export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
|
|
8
|
+
export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, type AddModuleOptions, type ResolvePaidModulesOptions, type ScaffoldOptions, type ScaffoldResult, type SyncArchitectureOptions, type SyncArchitectureResult, type SyncCatalogResult, type SyncRoutesSyncOptions, } from "./programmatic.js";
|
|
9
9
|
export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, type FetchModuleOptions, } from "./lib/fetch-module.js";
|
|
10
|
-
export { extractModuleBundle, type ExtractModuleBundleOptions, } from "./lib/extract-module-bundle.js";
|
|
10
|
+
export { extractModuleBundle, type ExistingFilePolicy, type ExtractModuleBundleOptions, type InstallReportRow, } from "./lib/extract-module-bundle.js";
|
|
11
|
+
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, type AddModuleReportRow, type AddModuleResult, type ApplyModuleOptions, } from "./add-module.js";
|
|
12
|
+
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
11
13
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
|
|
12
14
|
export { deriveArchitecture } from "./derive-architecture.js";
|
|
13
15
|
export { CORE_ROUTE_DIRS, CORE_SURFACE_PATHS, NAV_CONFIG_PATH, GATEABLE_SURFACES, MODULE_SURFACES, RECOGNIZED_SURFACES, ROUTE_SINCE, SurfaceError, excludedContentPaths, excludedRoutePaths, parseSurfaces, renderNavConfig, resolveSurfaces, surfaceForRouteDir, type Surface, } from "./surfaces.js";
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { copyTemplate } from "./copy.js";
|
|
7
7
|
export { parseCliArgs, parseModules, ParseError, helpText, RECOGNIZED_MODULES, } from "./parse-args.js";
|
|
8
|
-
export { CATALOG_TARGET_PATH, FREE_MODULES, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
8
|
+
export { CATALOG_TARGET_PATH, FREE_MODULES, addModule, LicenseRequiredError, PAID_MODULES, classifyModules, formatSurfaceDrift, scaffold, scaffoldSync, syncArchitectureSync, syncCatalogSync, syncRoutesSync, } from "./programmatic.js";
|
|
9
9
|
// The paid-module fetch/extract primitives (K1, #5089 — Flavor 3). Exported
|
|
10
10
|
// so tooling (and this repo's own evidence-run scripts) can drive the fetch
|
|
11
11
|
// path directly without re-implementing its shape.
|
|
12
12
|
export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, } from "./lib/fetch-module.js";
|
|
13
13
|
export { extractModuleBundle, } from "./lib/extract-module-bundle.js";
|
|
14
|
+
// `create-ailk add-module` (#6653) — apply a purchased module to an existing
|
|
15
|
+
// site in place. The pure core, beside `sync-routes`'s.
|
|
16
|
+
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, } from "./add-module.js";
|
|
17
|
+
// The site's page roster of record and its install-diff check (#6682).
|
|
18
|
+
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
14
19
|
// `create-ailk sync-routes` (#4986) — the template-route drift check + copy.
|
|
15
20
|
// The pure module is exported so tooling can run it against any template dir.
|
|
16
21
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, } from "./sync-routes.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ModulePatch } from "../module-architecture.js";
|
|
1
2
|
import { type TemplateLayout } from "../site-chrome.js";
|
|
2
3
|
import { type MarkerMap, type WorkspaceVersionMap } from "../substitute.js";
|
|
3
4
|
/** Exported for `fetch-template.ts`/`fetch-module.ts`'s Content-Length guard (#5514) — one cap, not two literals. */
|
|
@@ -44,6 +45,10 @@ export interface ExtractModuleBundleOptions {
|
|
|
44
45
|
ownership?: LayerOwnership;
|
|
45
46
|
/** The scaffold's marker map (#6460). See `InstallBundleTreeOptions`. */
|
|
46
47
|
markers?: MarkerMap;
|
|
48
|
+
/** What to do with a destination that already exists (#6653 D3). See `InstallBundleTreeOptions`. */
|
|
49
|
+
existingFilePolicy?: ExistingFilePolicy;
|
|
50
|
+
/** A caller check on the manifest's `patches` (#6653 D3). See `InstallBundleTreeOptions`. */
|
|
51
|
+
checkPatches?: (patches: readonly ModulePatch[]) => void;
|
|
47
52
|
}
|
|
48
53
|
/**
|
|
49
54
|
* Extract a fetched module bundle into `targetDir`, merging its files onto
|
|
@@ -108,8 +113,47 @@ export interface InstallBundleTreeOptions {
|
|
|
108
113
|
* files; `substituteMarkers` leaves every other `{{KEY}}` alone.
|
|
109
114
|
*/
|
|
110
115
|
markers?: MarkerMap;
|
|
116
|
+
/**
|
|
117
|
+
* What to do with a destination that already exists (#6653 D3). Default
|
|
118
|
+
* `"overwrite"` — the scaffold's layer-over-layer behaviour, unchanged.
|
|
119
|
+
* `"skip-existing"` — the `add-module` merge onto a tree a customer has
|
|
120
|
+
* lived in: an existing file is never written; when its bytes differ from
|
|
121
|
+
* what this install would write it is reported `skipped` with the change
|
|
122
|
+
* the bundle wanted, and when they are equal it is not reported at all.
|
|
123
|
+
* `patches` are NOT gated by it — they are the one mechanism designed to
|
|
124
|
+
* edit an existing file, and they are already idempotent.
|
|
125
|
+
*/
|
|
126
|
+
existingFilePolicy?: ExistingFilePolicy;
|
|
127
|
+
/**
|
|
128
|
+
* Run against the manifest's `patches` after they validate and BEFORE any
|
|
129
|
+
* of them is computed or anything is written (#6653 D3) — a throw rejects
|
|
130
|
+
* the tree with `targetDir` untouched. `add-module` uses it to hold a
|
|
131
|
+
* bundle's patches to the plus-config allowlist.
|
|
132
|
+
*/
|
|
133
|
+
checkPatches?: (patches: readonly ModulePatch[]) => void;
|
|
111
134
|
}
|
|
112
|
-
/**
|
|
135
|
+
/** See `InstallBundleTreeOptions.existingFilePolicy`. */
|
|
136
|
+
export type ExistingFilePolicy = "overwrite" | "skip-existing";
|
|
137
|
+
/**
|
|
138
|
+
* One file this install landed, patched, or declined to land (#6653 D3). A
|
|
139
|
+
* destination whose bytes already equal the install's produces no row.
|
|
140
|
+
* `change` / `wanted` is a unified diff (`diffTwoStrings`) — for `skipped`,
|
|
141
|
+
* the exact edit the bundle wanted, so the row is a manual-review item and
|
|
142
|
+
* never a bare path.
|
|
143
|
+
*/
|
|
144
|
+
export type InstallReportRow = {
|
|
145
|
+
path: string;
|
|
146
|
+
action: "written";
|
|
147
|
+
} | {
|
|
148
|
+
path: string;
|
|
149
|
+
action: "patched";
|
|
150
|
+
change: string;
|
|
151
|
+
} | {
|
|
152
|
+
path: string;
|
|
153
|
+
action: "skipped";
|
|
154
|
+
wanted: string;
|
|
155
|
+
};
|
|
156
|
+
/** `installBundleTree`'s result. */
|
|
113
157
|
export interface InstallBundleTreeResult {
|
|
114
158
|
/**
|
|
115
159
|
* The installed template's validated `layout` block (#5612 D1), unresolved
|
|
@@ -117,6 +161,8 @@ export interface InstallBundleTreeResult {
|
|
|
117
161
|
* module install, or a template whose manifest declares no `layout`.
|
|
118
162
|
*/
|
|
119
163
|
layout?: TemplateLayout;
|
|
164
|
+
/** One row per file written, patched or skipped, in install order (#6653 D3). */
|
|
165
|
+
report: InstallReportRow[];
|
|
120
166
|
}
|
|
121
167
|
/**
|
|
122
168
|
* Apply one resolved manifest's `installMap`, plain content and `patches`
|
|
@@ -129,3 +175,12 @@ export interface InstallBundleTreeResult {
|
|
|
129
175
|
* rejected tree leaves `targetDir` exactly as it was.
|
|
130
176
|
*/
|
|
131
177
|
export declare function installBundleTree(options: InstallBundleTreeOptions): InstallBundleTreeResult;
|
|
178
|
+
/**
|
|
179
|
+
* A unified diff of `before` → `after` for the report (#6653 D3) — the one
|
|
180
|
+
* helper behind both a `skipped` row's `wanted` and a `patched` row's
|
|
181
|
+
* `change`. Line-based LCS; the inputs are config and content files, small
|
|
182
|
+
* enough that the quadratic table is not a concern. `isText` false yields the
|
|
183
|
+
* binary summary instead, reading each string's length as its byte count
|
|
184
|
+
* (the caller decodes binary content as latin1).
|
|
185
|
+
*/
|
|
186
|
+
export declare function diffTwoStrings(path: string, before: string, after: string, isText: boolean): string;
|