create-ailk 0.18.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +6 -5
- package/NOTICE +2 -0
- package/dist/add-module.js +11 -3
- package/dist/category-profile.d.ts +10 -0
- package/dist/category-profile.js +21 -0
- package/dist/derive-architecture.d.ts +5 -1
- package/dist/derive-architecture.js +8 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/license-key-files.d.ts +6 -3
- package/dist/lib/license-key-files.js +8 -2
- package/dist/lib/scaffold-render-sweep.d.ts +32 -0
- package/dist/lib/scaffold-render-sweep.js +76 -3
- package/dist/license-set.d.ts +1 -1
- package/dist/license-set.js +13 -8
- package/dist/module-architecture.js +128 -3
- package/dist/programmatic.js +11 -1
- package/dist/site-roster.js +21 -4
- package/dist/spoke-checkout.d.ts +11 -3
- package/dist/spoke-checkout.js +62 -12
- package/dist/starter-grid.d.ts +1 -1
- package/dist/surfaces.d.ts +14 -0
- package/dist/surfaces.js +170 -1
- package/dist/template-manifest.d.ts +4 -3
- package/dist/topology.d.ts +7 -2
- package/dist/topology.js +88 -3
- package/package.json +5 -4
- package/templates/.env.example +3 -3
- package/templates/.github/workflows/ci.yml +5 -0
- package/templates/apps/api/.env.example +17 -1
- package/templates/apps/api/CLAUDE.md +31 -7
- package/templates/apps/api/LICENSE +6 -5
- package/templates/apps/api/NOTICE +2 -0
- package/templates/apps/api/jest.config.cjs +4 -29
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +19 -0
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +132 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +17 -0
- package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +237 -0
- package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +334 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +38 -0
- package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +217 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +7 -0
- package/templates/apps/api/src/lib/consent-store.ts +335 -0
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +5 -4
- package/templates/apps/api/src/lib/stripe.ts +176 -3
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +272 -39
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +51 -0
- package/templates/apps/api/src/middleware/auth.ts +214 -46
- package/templates/apps/api/src/middleware/rate-limit.ts +5 -0
- package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +167 -0
- package/templates/apps/api/src/routes/checkout/session.ts +3 -3
- package/templates/apps/api/src/routes/checkout/sessions.ts +6 -4
- package/templates/apps/api/src/routes/consent/cookie.ts +90 -0
- package/templates/apps/api/src/routes/consent/index.ts +18 -0
- package/templates/apps/api/src/routes/consent/unsubscribe.ts +127 -0
- package/templates/apps/api/src/routes/leads/index.ts +19 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +5 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +17 -4
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +2 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +159 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +158 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +2 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +17 -0
- package/templates/apps/api/src/server.ts +218 -16
- package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +503 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +18 -3
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +173 -4
- package/templates/apps/api/src/services/__tests__/person.test.ts +392 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +87 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +57 -17
- package/templates/apps/api/src/services/consent.ts +488 -3
- package/templates/apps/api/src/services/lead-routing.ts +135 -14
- package/templates/apps/api/src/services/person.ts +501 -1
- package/templates/apps/api/src/services/project-listings.ts +7 -2
- package/templates/apps/api/src/services/request-confirm.ts +52 -0
- package/templates/apps/api/src/services/revalidation.ts +11 -1
- package/templates/apps/api/vercel.json +2 -2
- package/templates/apps/mcp/.env.example +10 -0
- package/templates/apps/mcp/CLAUDE.md +9 -4
- package/templates/apps/mcp/LICENSE +6 -5
- package/templates/apps/mcp/NOTICE +2 -0
- package/templates/apps/mcp/__tests__/abuse/guard.test.ts +198 -0
- package/templates/apps/mcp/__tests__/audit-single-page.test.ts +15 -3
- package/templates/apps/mcp/__tests__/boot.test.ts +78 -1
- package/templates/apps/mcp/__tests__/http-transport.test.ts +291 -2
- package/templates/apps/mcp/__tests__/request_callback.test.ts +69 -7
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +69 -8
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +69 -7
- package/templates/apps/mcp/jest.config.cjs +3 -44
- package/templates/apps/mcp/src/abuse/index.ts +117 -21
- package/templates/apps/mcp/src/boot.ts +5 -23
- package/templates/apps/mcp/src/http-transport.ts +93 -73
- package/templates/apps/mcp/src/index.ts +2 -0
- package/templates/apps/mcp/src/request-context.ts +52 -0
- package/templates/apps/mcp/src/tools/capture_lead.ts +9 -1
- package/templates/apps/mcp/src/tools/request_callback.ts +9 -1
- package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +9 -1
- package/templates/apps/web/LICENSE +6 -5
- package/templates/apps/web/NOTICE +2 -0
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/account-frame-sign-out.test.tsx +175 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +302 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +724 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +346 -41
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +11 -13
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +146 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +232 -29
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +305 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-popups.test.tsx +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +140 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +163 -72
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +451 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +409 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +65 -380
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/support.test.tsx +16 -11
- package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +1 -1
- package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +188 -54
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +24 -45
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +253 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +145 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +100 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +179 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +514 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +288 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +123 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +174 -0
- package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +172 -0
- package/templates/apps/web/app/[locale]/(authed)/account/billing-source.ts +147 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/request-quote.tsx +229 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/schedule-service.tsx +276 -0
- package/templates/apps/web/app/[locale]/(authed)/account/components/send-message.tsx +128 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/billing-anchor.tsx +27 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/contact-actions.ts +81 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/deletion-request.tsx +132 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +193 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/profile-form.tsx +104 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/profile-source.ts +73 -0
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +18 -8
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +4 -6
- package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +30 -4
- package/templates/apps/web/app/[locale]/(authed)/account/new-actions.ts +219 -0
- package/templates/apps/web/app/[locale]/(authed)/account/new-menu.tsx +340 -0
- package/templates/apps/web/app/[locale]/(authed)/account/new-popups.tsx +107 -0
- package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +57 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +85 -26
- package/templates/apps/web/app/[locale]/(authed)/account/profile-actions.ts +116 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +132 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/security-panel.tsx +102 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/security-source.ts +62 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +13 -354
- package/templates/apps/web/app/[locale]/(authed)/account/settings-frame.tsx +116 -0
- package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +37 -0
- package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +9 -10
- package/templates/apps/web/app/[locale]/layout.tsx +23 -4
- package/templates/apps/web/app/api/auth/hub/__tests__/continuation.test.ts +143 -0
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +62 -10
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +22 -7
- package/templates/apps/web/app/api/auth/hub/continuation.ts +73 -0
- package/templates/apps/web/app/api/auth/hub/continue/[ref]/__tests__/route.test.ts +188 -0
- package/templates/apps/web/app/api/auth/hub/continue/[ref]/route.ts +53 -0
- package/templates/apps/web/app/robots.txt/route.ts +10 -4
- package/templates/apps/web/auth.ts +25 -2
- package/templates/apps/web/lib/__tests__/account-config.test.ts +258 -46
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +84 -0
- package/templates/apps/web/lib/account-config.ts +188 -52
- package/templates/apps/web/lib/auth-email.ts +45 -10
- package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +143 -0
- package/templates/apps/web/lib/hub-session/index.ts +1 -0
- package/templates/apps/web/lib/hub-session/reseal.ts +90 -0
- package/templates/apps/web/lib/hub-session/seal.ts +13 -9
- package/templates/apps/web/lib/topology.generated.ts +33 -0
- package/templates/apps/web/messages/en.json +194 -33
- package/templates/database/CHANGELOG.md +37 -0
- package/templates/database/LICENSE +6 -5
- package/templates/database/NOTICE +2 -0
- package/templates/database/README.md +1 -1
- package/templates/database/__tests__/consent-ledger.db.test.ts +345 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +6 -2
- package/templates/database/__tests__/fixtures/person-contact-backfill.json +182 -0
- package/templates/database/__tests__/member-area.db.test.ts +15 -3
- package/templates/database/__tests__/member-contract-migration.test.ts +85 -0
- package/templates/database/__tests__/member-contract.db.test.ts +413 -0
- package/templates/database/__tests__/person-contact.db.test.ts +563 -0
- package/templates/database/__tests__/person.db.test.ts +3 -1
- package/templates/database/__tests__/seed.test.ts +54 -2
- package/templates/database/inbox/schema.prisma +166 -25
- package/templates/database/migrations/20261012120000_member_contract/migration.sql +192 -0
- package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +20 -0
- package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +122 -0
- package/templates/database/migrations/20261014120000_person_contact/migration.sql +231 -0
- package/templates/database/package.json +3 -2
- package/templates/database/seed.ts +34 -2
- package/versions.json +36 -36
- package/templates/apps/api/src/__tests__/content-write-production-boot.test.ts +0 -104
- package/templates/apps/api/src/__tests__/cors.test.ts +0 -276
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +0 -279
- package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +0 -122
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +0 -167
- package/templates/apps/api/src/__tests__/root-modules.test.ts +0 -175
- package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +0 -117
- package/templates/apps/api/src/__tests__/server.test.ts +0 -591
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +0 -175
- package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +0 -293
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +0 -1620
- package/templates/apps/api/src/routes/project-listings/__tests__/directory.test.ts +0 -528
- package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +0 -198
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +0 -652
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -408
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +0 -476
- package/templates/apps/mcp/__tests__/audit.test.ts +0 -346
- package/templates/apps/mcp/__tests__/parity.test.ts +0 -325
- package/templates/apps/mcp/__tests__/workspace-surface.test.ts +0 -364
- package/templates/apps/web/app/[locale]/(authed)/account/settings/color-mode.tsx +0 -68
- package/templates/apps/web/app/[locale]/(authed)/account/settings/deletion-request.tsx +0 -55
- package/templates/apps/web/app/[locale]/(authed)/account/settings/name-form.tsx +0 -118
- package/templates/database/__tests__/build.test.ts +0 -131
package/LICENSE
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -137,8 +138,8 @@
|
|
|
137
138
|
|
|
138
139
|
6. Trademarks. This License does not grant permission to use the trade
|
|
139
140
|
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
-
except as required for
|
|
141
|
-
reproducing the content of the NOTICE file.
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
143
|
|
|
143
144
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
145
|
agreed to in writing, Licensor provides the Work (and each
|
|
@@ -162,7 +163,7 @@
|
|
|
162
163
|
other commercial damages or losses), even if such Contributor
|
|
163
164
|
has been advised of the possibility of such damages.
|
|
164
165
|
|
|
165
|
-
9. Accepting Warranty or
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
167
|
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
168
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
169
|
or other liability obligations and/or rights consistent with this
|
|
@@ -171,7 +172,7 @@
|
|
|
171
172
|
of any other Contributor, and only if You agree to indemnify,
|
|
172
173
|
defend, and hold each Contributor harmless for any liability
|
|
173
174
|
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
-
of your accepting any such warranty or
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
175
176
|
|
|
176
177
|
END OF TERMS AND CONDITIONS
|
|
177
178
|
|
|
@@ -186,7 +187,7 @@
|
|
|
186
187
|
same "printed page" as the copyright notice for easier
|
|
187
188
|
identification within third-party archives.
|
|
188
189
|
|
|
189
|
-
Copyright
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
191
|
|
|
191
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
193
|
you may not use this file except in compliance with the License.
|
package/NOTICE
ADDED
package/dist/add-module.js
CHANGED
|
@@ -40,7 +40,7 @@ import { ParseError, parseModules } from "./parse-args.js";
|
|
|
40
40
|
import { writeSpokeFlowsReadme } from "./seed-flows.js";
|
|
41
41
|
import { SITE_ROSTER_PATH, moduleRosterRows, planRosterAppend, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
42
42
|
import { spokeCheckoutSite, spokeEnvRowOptions, writeSpokeCheckoutSlice, } from "./spoke-checkout.js";
|
|
43
|
-
import { MODULE_SURFACES, NAV_CONFIG_PATH, readNavConfigStamp, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
43
|
+
import { MODULE_SURFACES, NAV_CONFIG_PATH, isUnservedSurfaceContent, readNavConfigStamp, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
44
44
|
import { syncRoutes } from "./sync-routes.js";
|
|
45
45
|
import { TopologyError, isSpokeExcluded, readSiteTopology, readSpokeShape, spokeDestination, spokeModuleRefusal, } from "./topology.js";
|
|
46
46
|
/**
|
|
@@ -155,9 +155,14 @@ export async function applyModuleToScaffold(opts) {
|
|
|
155
155
|
markers: opts.markers,
|
|
156
156
|
existingFilePolicy: "skip-existing",
|
|
157
157
|
checkPatches: checkPlusConfigPatches,
|
|
158
|
+
// #8427 D4 — composed with the spoke filter: a module bundle never
|
|
159
|
+
// writes a seed under a gateable surface this site's own
|
|
160
|
+
// `architecture.yaml` header (resolved above, into `surfaces`) does
|
|
161
|
+
// not enable.
|
|
162
|
+
skip: (destination) => (spoke !== undefined && isSpokeExcluded(destination, spoke)) ||
|
|
163
|
+
isUnservedSurfaceContent(destination, surfaces),
|
|
158
164
|
...(spoke !== undefined
|
|
159
165
|
? {
|
|
160
|
-
skip: (destination) => isSpokeExcluded(destination, spoke),
|
|
161
166
|
// #7876 D10 — a module flow file lands in flows/ as declared data.
|
|
162
167
|
remapDestination: spokeDestination,
|
|
163
168
|
}
|
|
@@ -165,12 +170,15 @@ export async function applyModuleToScaffold(opts) {
|
|
|
165
170
|
});
|
|
166
171
|
// #7838 D8 — commerce on a spoke brings the spoke's own checkout, stamped
|
|
167
172
|
// from the site directory's name (the `projectName` scaffold() defaults to).
|
|
168
|
-
// A file the site already has is left as it is.
|
|
173
|
+
// A file the site already has is left as it is. #8217 D8 — on a spoke that
|
|
174
|
+
// signs in through its hub, the billing action and its seam render too, so
|
|
175
|
+
// re-running add-module commerce gives an existing commerce spoke the action.
|
|
169
176
|
const spokeCommerce = spoke !== undefined && opts.moduleName === "commerce";
|
|
170
177
|
const checkoutRows = spokeCommerce
|
|
171
178
|
? writeSpokeCheckoutSlice(targetAbs, {
|
|
172
179
|
site: spokeCheckoutSite(basename(targetAbs)),
|
|
173
180
|
versions: opts.versions,
|
|
181
|
+
signIn: spoke.signIn,
|
|
174
182
|
})
|
|
175
183
|
: [];
|
|
176
184
|
// #7876 D9 — a spoke that just gained the flow stepper says what its
|
|
@@ -50,6 +50,16 @@ export declare const CATEGORY_PROFILE_SHAPES: {
|
|
|
50
50
|
* set-equal to the `schema-dts` closure, so it cannot drift silently.
|
|
51
51
|
*/
|
|
52
52
|
export declare const LOCAL_BUSINESS_TYPES: ReadonlySet<string>;
|
|
53
|
+
/**
|
|
54
|
+
* The leaf category slugs, in registry order — create-ailk's own copy of the
|
|
55
|
+
* site-category registry's leaves (`SITE_CATEGORIES`, #5830). A literal, not
|
|
56
|
+
* a type import, so the emitted `.d.ts` of the public tarball names no
|
|
57
|
+
* `@working-theory/*` package (#8412); `category-slugs.test.ts` proves it
|
|
58
|
+
* equal to the registry, so it cannot drift silently.
|
|
59
|
+
*/
|
|
60
|
+
export declare const CATEGORY_SLUGS: readonly ["accounting", "legal", "consulting", "hvac", "roofing", "plumbing", "electrical", "landscaping", "auto-repair", "real-estate", "technology", "medical-specialty"];
|
|
61
|
+
/** A leaf category slug — the only thing a template's `categories` names. */
|
|
62
|
+
export type CategorySlug = (typeof CATEGORY_SLUGS)[number];
|
|
53
63
|
export declare const OFFERING_PRICING: readonly ["quote", "fixed", "hourly", "subscription"];
|
|
54
64
|
export declare const PROOF_KINDS: readonly ["credential", "reviews", "before-after", "response-time", "guarantee", "experience"];
|
|
55
65
|
export declare const CONTENT_POSTURES: readonly ["instructions", "sample"];
|
package/dist/category-profile.js
CHANGED
|
@@ -71,6 +71,27 @@ export const LOCAL_BUSINESS_TYPES = new Set([
|
|
|
71
71
|
"TennisComplex", "TireShop", "TouristInformationCenter", "ToyStore",
|
|
72
72
|
"TravelAgency", "VacationRental", "WholesaleStore", "Winery",
|
|
73
73
|
]);
|
|
74
|
+
/**
|
|
75
|
+
* The leaf category slugs, in registry order — create-ailk's own copy of the
|
|
76
|
+
* site-category registry's leaves (`SITE_CATEGORIES`, #5830). A literal, not
|
|
77
|
+
* a type import, so the emitted `.d.ts` of the public tarball names no
|
|
78
|
+
* `@working-theory/*` package (#8412); `category-slugs.test.ts` proves it
|
|
79
|
+
* equal to the registry, so it cannot drift silently.
|
|
80
|
+
*/
|
|
81
|
+
export const CATEGORY_SLUGS = [
|
|
82
|
+
"accounting",
|
|
83
|
+
"legal",
|
|
84
|
+
"consulting",
|
|
85
|
+
"hvac",
|
|
86
|
+
"roofing",
|
|
87
|
+
"plumbing",
|
|
88
|
+
"electrical",
|
|
89
|
+
"landscaping",
|
|
90
|
+
"auto-repair",
|
|
91
|
+
"real-estate",
|
|
92
|
+
"technology",
|
|
93
|
+
"medical-specialty",
|
|
94
|
+
];
|
|
74
95
|
export const OFFERING_PRICING = ["quote", "fixed", "hourly", "subscription"];
|
|
75
96
|
export const PROOF_KINDS = [
|
|
76
97
|
"credential",
|
|
@@ -22,7 +22,11 @@ export interface DeriveArchitectureOptions {
|
|
|
22
22
|
/** Spoke only: whether the spoke keeps `apps/mcp` (default true). */
|
|
23
23
|
mcp?: boolean;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* The only spoke files that may import @working-theory/spoke-checkout (#7873):
|
|
27
|
+
* the buy route, the webhook route and — on a spoke that signs in through its
|
|
28
|
+
* hub — the billing action (#8217 D9, a reviewed change to the #7873 rule).
|
|
29
|
+
*/
|
|
26
30
|
export declare const SPOKE_CHECKOUT_ROUTE_FILES: readonly string[];
|
|
27
31
|
/** The forbidden patterns a spoke's apps/web carries (#7873, brief Part 2 §2). */
|
|
28
32
|
export declare const SPOKE_WEB_FORBIDDEN_PATTERNS: readonly ForbiddenPattern[];
|
|
@@ -27,10 +27,15 @@ const SPOKE_TIERS = {
|
|
|
27
27
|
// import only to a workspace on disk, so the one way to ban an npm package is
|
|
28
28
|
// a line-level forbidden pattern. These go under the spoke's own `apps.web`
|
|
29
29
|
// and `apps.mcp` only — never top level, and never on a hub derivation.
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The only spoke files that may import @working-theory/spoke-checkout (#7873):
|
|
32
|
+
* the buy route, the webhook route and — on a spoke that signs in through its
|
|
33
|
+
* hub — the billing action (#8217 D9, a reviewed change to the #7873 rule).
|
|
34
|
+
*/
|
|
31
35
|
export const SPOKE_CHECKOUT_ROUTE_FILES = Object.freeze([
|
|
32
36
|
"apps/web/app/api/checkout/[offering]/route.ts",
|
|
33
37
|
"apps/web/app/api/webhooks/stripe/route.ts",
|
|
38
|
+
"apps/web/app/[locale]/(authed)/account/billing/spoke-billing.action.ts",
|
|
34
39
|
]);
|
|
35
40
|
/**
|
|
36
41
|
* A line-level regex for an import of `specifier` or one of its subpaths, in
|
|
@@ -45,10 +50,10 @@ function specifierForm(specifier) {
|
|
|
45
50
|
const literal = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46
51
|
return `\\b(?:from|import|require)\\s*\\(?\\s*${q}${literal}(?:/[^'"\`]*)?${q}`;
|
|
47
52
|
}
|
|
48
|
-
/** P1 with no exclusions — a kept apps/mcp's form; apps/web adds the
|
|
53
|
+
/** P1 with no exclusions — a kept apps/mcp's form; apps/web adds the route files. */
|
|
49
54
|
const SPOKE_CHECKOUT_PATTERN = Object.freeze({
|
|
50
55
|
pattern: specifierForm("@working-theory/spoke-checkout"),
|
|
51
|
-
reason: "Spoke checkout: only the buy route
|
|
56
|
+
reason: "Spoke checkout: only the buy route, the webhook route and the billing action may import @working-theory/spoke-checkout (brief Part 2 §2, #7873, #8217)",
|
|
52
57
|
});
|
|
53
58
|
const STRIPE_CLIENT_PATTERN = Object.freeze({
|
|
54
59
|
pattern: specifierForm("@working-theory/stripe-client"),
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export type { HomeSeedOutcome } from "./design-home.js";
|
|
|
14
14
|
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
15
15
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
|
|
16
16
|
export { deriveArchitecture } from "./derive-architecture.js";
|
|
17
|
-
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";
|
|
17
|
+
export { CORE_ROUTE_DIRS, CORE_SURFACE_PATHS, NAV_CONFIG_PATH, GATEABLE_SURFACES, MODULE_SURFACES, RECOGNIZED_SURFACES, ROUTE_SINCE, SurfaceError, excludedContentPaths, excludedRoutePaths, isUnservedSurfaceContent, parseSurfaces, renderNavConfig, resolveSurfaces, surfaceForRouteDir, type Surface, } from "./surfaces.js";
|
|
18
18
|
export { substituteMarkers, substituteWorkspaceVersions, type MarkerMap, type WorkspaceVersionMap, } from "./substitute.js";
|
|
19
19
|
export { validateTarget, ValidationError } from "./validate.js";
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,6 @@ export { deriveArchitecture } from "./derive-architecture.js";
|
|
|
30
30
|
// The route-surface registry (#4154) — which public routes a scaffold carries.
|
|
31
31
|
// Exported so tooling can answer "what surfaces does this site declare" without
|
|
32
32
|
// re-parsing architecture.yaml.
|
|
33
|
-
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, } from "./surfaces.js";
|
|
33
|
+
export { CORE_ROUTE_DIRS, CORE_SURFACE_PATHS, NAV_CONFIG_PATH, GATEABLE_SURFACES, MODULE_SURFACES, RECOGNIZED_SURFACES, ROUTE_SINCE, SurfaceError, excludedContentPaths, excludedRoutePaths, isUnservedSurfaceContent, parseSurfaces, renderNavConfig, resolveSurfaces, surfaceForRouteDir, } from "./surfaces.js";
|
|
34
34
|
export { substituteMarkers, substituteWorkspaceVersions, } from "./substitute.js";
|
|
35
35
|
export { validateTarget, ValidationError } from "./validate.js";
|
|
@@ -21,9 +21,12 @@ export declare function appendLine(text: string, line: string): string;
|
|
|
21
21
|
* `ailk.key` when no existing line covers it. Idempotent — a second call with
|
|
22
22
|
* the same key changes no byte.
|
|
23
23
|
*
|
|
24
|
-
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6)
|
|
25
|
-
* `apps/api
|
|
24
|
+
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6) or any
|
|
25
|
+
* site without `apps/api` (#7779) keeps the key in `ailk.key` alone.
|
|
26
|
+
*
|
|
27
|
+
* Returns the key files it holds the key in, relative to `dir`
|
|
28
|
+
* (`LICENSE_KEY_FILE`, then `LICENSE_ENV_FILE` when written).
|
|
26
29
|
*/
|
|
27
30
|
export declare function writeLicenseKeyFiles(dir: string, key: string, opts?: {
|
|
28
31
|
apiEnv?: boolean;
|
|
29
|
-
}):
|
|
32
|
+
}): string[];
|
|
@@ -86,16 +86,21 @@ function upsertEnvLine(existing, key) {
|
|
|
86
86
|
* `ailk.key` when no existing line covers it. Idempotent — a second call with
|
|
87
87
|
* the same key changes no byte.
|
|
88
88
|
*
|
|
89
|
-
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6)
|
|
90
|
-
* `apps/api
|
|
89
|
+
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6) or any
|
|
90
|
+
* site without `apps/api` (#7779) keeps the key in `ailk.key` alone.
|
|
91
|
+
*
|
|
92
|
+
* Returns the key files it holds the key in, relative to `dir`
|
|
93
|
+
* (`LICENSE_KEY_FILE`, then `LICENSE_ENV_FILE` when written).
|
|
91
94
|
*/
|
|
92
95
|
export function writeLicenseKeyFiles(dir, key, opts = {}) {
|
|
93
96
|
assertLicenseKeyShape(key);
|
|
94
97
|
writeOwnerOnly(join(dir, LICENSE_KEY_FILE), `${key}\n`);
|
|
98
|
+
const written = [LICENSE_KEY_FILE];
|
|
95
99
|
if (opts.apiEnv !== false) {
|
|
96
100
|
const envPath = join(dir, LICENSE_ENV_FILE);
|
|
97
101
|
const env = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
98
102
|
writeOwnerOnly(envPath, upsertEnvLine(env, key));
|
|
103
|
+
written.push(LICENSE_ENV_FILE);
|
|
99
104
|
}
|
|
100
105
|
const ignorePath = join(dir, ".gitignore");
|
|
101
106
|
const ignore = existsSync(ignorePath) ? readFileSync(ignorePath, "utf8") : "";
|
|
@@ -104,4 +109,5 @@ export function writeLicenseKeyFiles(dir, key, opts = {}) {
|
|
|
104
109
|
.some((l) => KEY_IGNORE_LINES.has(l.trim()));
|
|
105
110
|
if (!covered)
|
|
106
111
|
writeFileSync(ignorePath, appendLine(ignore, LICENSE_KEY_FILE));
|
|
112
|
+
return written;
|
|
107
113
|
}
|
|
@@ -53,6 +53,15 @@ export declare function assessClaimGatedRoute(input: {
|
|
|
53
53
|
hops: RedirectHop[];
|
|
54
54
|
status: number | null;
|
|
55
55
|
}): SweepVerdict;
|
|
56
|
+
/**
|
|
57
|
+
* A `redirect()` thrown by a Server Component after the shell has flushed is
|
|
58
|
+
* not an HTTP 3xx: the response is a 200 whose flight payload carries the
|
|
59
|
+
* digest `NEXT_REDIRECT;<replace|push>;<url>;<status>;`. The driver follows it
|
|
60
|
+
* as a hop exactly like a `Location` header (#8311 — `/account/settings`
|
|
61
|
+
* forwards to `/account/general` this way). Null when the HTML carries no
|
|
62
|
+
* such digest.
|
|
63
|
+
*/
|
|
64
|
+
export declare function streamedRedirect(html: string): RedirectHop | null;
|
|
56
65
|
/**
|
|
57
66
|
* Decide whether a page route rendered, under the real locale, with its own
|
|
58
67
|
* content. The first failed rule is the reason:
|
|
@@ -96,6 +105,20 @@ export declare function assessConfigGatedRoute(input: {
|
|
|
96
105
|
html: string;
|
|
97
106
|
locale: string;
|
|
98
107
|
}): SweepVerdict;
|
|
108
|
+
/**
|
|
109
|
+
* The environment the sitemap sweep's server runs under (#8501): `env` with
|
|
110
|
+
* every `CONFIG_GATED_ROUTES` variable removed.
|
|
111
|
+
*
|
|
112
|
+
* `/sitemap.xml` is prerendered at `next build`, from the content seeds, so it
|
|
113
|
+
* describes the scaffold AS GENERATED — and a config-gated route's seeds are
|
|
114
|
+
* served only while its variable is unset (the Free placeholder hand-off,
|
|
115
|
+
* #6942). Set it and the route serves its live module page instead: `/meet`
|
|
116
|
+
* becomes the booker for the operator's enabled events, `/meet/<seed>` a 404.
|
|
117
|
+
* A leg that configures the module for its booking steps would therefore score
|
|
118
|
+
* the sitemap against pages it was never written for, so the sweep runs
|
|
119
|
+
* against a server that leaves the gated variables unset. Pure.
|
|
120
|
+
*/
|
|
121
|
+
export declare function sitemapSweepEnv(env: Readonly<Record<string, string | undefined>>): Record<string, string | undefined>;
|
|
99
122
|
/**
|
|
100
123
|
* Every FILE under `dir`, recursing into directories, as sorted slash-joined
|
|
101
124
|
* paths relative to it — so `public/.well-known/security.txt` is listed and
|
|
@@ -115,6 +138,15 @@ export declare function assessUnknownSlug(input: {
|
|
|
115
138
|
hops: RedirectHop[];
|
|
116
139
|
status: number | null;
|
|
117
140
|
}): SweepVerdict;
|
|
141
|
+
/**
|
|
142
|
+
* Every `<loc>` path in a sitemap XML document, in order, deduped (#8427
|
|
143
|
+
* D6). Pure — no network or filesystem access, like its neighbours. Each
|
|
144
|
+
* `<loc>` is un-escaped against the five entities `escapeXml` writes, then
|
|
145
|
+
* read as `new URL(loc).pathname` — so a query string contributes nothing
|
|
146
|
+
* and an `<xhtml:link rel="alternate">` sibling, which carries no `<loc>` of
|
|
147
|
+
* its own, contributes nothing either.
|
|
148
|
+
*/
|
|
149
|
+
export declare function sitemapLocPaths(xml: string): string[];
|
|
118
150
|
/** Fails, naming each route, when `.next/prerender-manifest.json`'s `dynamicRoutes` has a key under `/[locale]/`. */
|
|
119
151
|
export declare function assessPrerenderManifest(manifest: {
|
|
120
152
|
dynamicRoutes?: Record<string, unknown>;
|
|
@@ -122,6 +122,24 @@ function htmlLang(html) {
|
|
|
122
122
|
function flightDigest(html) {
|
|
123
123
|
return /\\"digest\\":\\"([^"\\]*)\\"/.exec(html)?.[1] ?? null;
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* A `redirect()` thrown by a Server Component after the shell has flushed is
|
|
127
|
+
* not an HTTP 3xx: the response is a 200 whose flight payload carries the
|
|
128
|
+
* digest `NEXT_REDIRECT;<replace|push>;<url>;<status>;`. The driver follows it
|
|
129
|
+
* as a hop exactly like a `Location` header (#8311 — `/account/settings`
|
|
130
|
+
* forwards to `/account/general` this way). Null when the HTML carries no
|
|
131
|
+
* such digest.
|
|
132
|
+
*/
|
|
133
|
+
export function streamedRedirect(html) {
|
|
134
|
+
const digest = flightDigest(html);
|
|
135
|
+
if (digest === null || !digest.startsWith("NEXT_REDIRECT;"))
|
|
136
|
+
return null;
|
|
137
|
+
const [, , location = "", status = ""] = digest.split(";");
|
|
138
|
+
if (location === "")
|
|
139
|
+
return null;
|
|
140
|
+
const code = Number.parseInt(status, 10);
|
|
141
|
+
return { status: Number.isFinite(code) ? code : 307, location };
|
|
142
|
+
}
|
|
125
143
|
/**
|
|
126
144
|
* Whether an `<h1>`'s inner HTML carries a name: text once tags are stripped,
|
|
127
145
|
* or an image with a non-empty `alt` (the Hero's lockup renders the wordmark
|
|
@@ -170,9 +188,12 @@ export function assessPageRender(input) {
|
|
|
170
188
|
return { ok: false, reason: `${route} -> rendered under locale "${lang}"` };
|
|
171
189
|
const digest = flightDigest(html);
|
|
172
190
|
if (digest !== null) {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
191
|
+
const redirect = streamedRedirect(html);
|
|
192
|
+
const why = redirect !== null
|
|
193
|
+
? `streamed redirect to ${redirect.location} that was not followed`
|
|
194
|
+
: digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;404")
|
|
195
|
+
? "streamed not-found"
|
|
196
|
+
: `streamed error (digest ${digest})`;
|
|
176
197
|
return { ok: false, reason: `${route} -> ${why}` };
|
|
177
198
|
}
|
|
178
199
|
const marker = ownContentMarker(html);
|
|
@@ -218,6 +239,23 @@ export function assessConfigGatedRoute(input) {
|
|
|
218
239
|
function headingTexts(html) {
|
|
219
240
|
return [...html.matchAll(/<h1\b[^>]*>([\s\S]*?)<\/h1>/gi)].map((m) => (m[1] ?? "").replace(/<[^>]*>/g, ""));
|
|
220
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* The environment the sitemap sweep's server runs under (#8501): `env` with
|
|
244
|
+
* every `CONFIG_GATED_ROUTES` variable removed.
|
|
245
|
+
*
|
|
246
|
+
* `/sitemap.xml` is prerendered at `next build`, from the content seeds, so it
|
|
247
|
+
* describes the scaffold AS GENERATED — and a config-gated route's seeds are
|
|
248
|
+
* served only while its variable is unset (the Free placeholder hand-off,
|
|
249
|
+
* #6942). Set it and the route serves its live module page instead: `/meet`
|
|
250
|
+
* becomes the booker for the operator's enabled events, `/meet/<seed>` a 404.
|
|
251
|
+
* A leg that configures the module for its booking steps would therefore score
|
|
252
|
+
* the sitemap against pages it was never written for, so the sweep runs
|
|
253
|
+
* against a server that leaves the gated variables unset. Pure.
|
|
254
|
+
*/
|
|
255
|
+
export function sitemapSweepEnv(env) {
|
|
256
|
+
const gated = new Set(Object.values(CONFIG_GATED_ROUTES));
|
|
257
|
+
return Object.fromEntries(Object.entries(env).filter(([name]) => !gated.has(name)));
|
|
258
|
+
}
|
|
221
259
|
/**
|
|
222
260
|
* Every FILE under `dir`, recursing into directories, as sorted slash-joined
|
|
223
261
|
* paths relative to it — so `public/.well-known/security.txt` is listed and
|
|
@@ -270,6 +308,41 @@ export function assessUnknownSlug(input) {
|
|
|
270
308
|
return { ok: false, reason: `${route} -> status ${status} (expected 404 for an unknown slug)` };
|
|
271
309
|
return { ok: true, reason: "404" };
|
|
272
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* The five entities `escapeXml` (`apps/web/app/sitemap.xml/route.ts`) writes,
|
|
313
|
+
* reversed.
|
|
314
|
+
*/
|
|
315
|
+
const XML_ENTITIES = {
|
|
316
|
+
"&": "&",
|
|
317
|
+
"<": "<",
|
|
318
|
+
">": ">",
|
|
319
|
+
""": '"',
|
|
320
|
+
"'": "'",
|
|
321
|
+
};
|
|
322
|
+
function unescapeXml(value) {
|
|
323
|
+
return value.replace(/&|<|>|"|'/g, (entity) => XML_ENTITIES[entity] ?? entity);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Every `<loc>` path in a sitemap XML document, in order, deduped (#8427
|
|
327
|
+
* D6). Pure — no network or filesystem access, like its neighbours. Each
|
|
328
|
+
* `<loc>` is un-escaped against the five entities `escapeXml` writes, then
|
|
329
|
+
* read as `new URL(loc).pathname` — so a query string contributes nothing
|
|
330
|
+
* and an `<xhtml:link rel="alternate">` sibling, which carries no `<loc>` of
|
|
331
|
+
* its own, contributes nothing either.
|
|
332
|
+
*/
|
|
333
|
+
export function sitemapLocPaths(xml) {
|
|
334
|
+
const seen = new Set();
|
|
335
|
+
const paths = [];
|
|
336
|
+
for (const match of xml.matchAll(/<loc>([^<]*)<\/loc>/g)) {
|
|
337
|
+
const loc = unescapeXml(match[1] ?? "");
|
|
338
|
+
const path = new URL(loc).pathname;
|
|
339
|
+
if (seen.has(path))
|
|
340
|
+
continue;
|
|
341
|
+
seen.add(path);
|
|
342
|
+
paths.push(path);
|
|
343
|
+
}
|
|
344
|
+
return paths;
|
|
345
|
+
}
|
|
273
346
|
/** Fails, naming each route, when `.next/prerender-manifest.json`'s `dynamicRoutes` has a key under `/[locale]/`. */
|
|
274
347
|
export function assessPrerenderManifest(manifest) {
|
|
275
348
|
const locale = Object.keys(manifest.dynamicRoutes ?? {})
|
package/dist/license-set.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface LicenseSetIO {
|
|
|
24
24
|
/** Defaults to `spawnSync` with `shell: false` (see `defaultRunner`). */
|
|
25
25
|
runner?: LicenseSetRunner;
|
|
26
26
|
}
|
|
27
|
-
export declare const LICENSE_SET_HELP = "create-ailk license set \u2014 put your licence key into an existing site.\n\nUsage:\n pnpm create ailk@latest license set <key> [<target-dir>]\n pnpm create ailk@latest license set - [<target-dir>] < key.txt\n\nArguments:\n <key> Your licence key, or `-` to read it from stdin\n (keeps it out of your shell history).\n <target-dir> An already-scaffolded repo (contains\n architecture.yaml). Defaults to the current dir.\n\nWrites the key to ailk.key and apps/api/.env (owner-only, gitignored). When the
|
|
27
|
+
export declare const LICENSE_SET_HELP = "create-ailk license set \u2014 put your licence key into an existing site.\n\nUsage:\n pnpm create ailk@latest license set <key> [<target-dir>]\n pnpm create ailk@latest license set - [<target-dir>] < key.txt\n\nArguments:\n <key> Your licence key, or `-` to read it from stdin\n (keeps it out of your shell history).\n <target-dir> An already-scaffolded repo (contains\n architecture.yaml). Defaults to the current dir.\n\nWrites the key to ailk.key, and to apps/api/.env when the site has apps/api\n(owner-only, gitignored). When the site (or apps/api, or apps/web) is linked to\nVercel, it also replaces the production AILK_LICENSE_KEY variable and redeploys.\n";
|
|
28
28
|
/** `spawnSync`, never through a shell; stdin piped only when there is input. */
|
|
29
29
|
export declare const defaultRunner: LicenseSetRunner;
|
|
30
30
|
/**
|
package/dist/license-set.js
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 1. The target (default: cwd) must contain `architecture.yaml` — the
|
|
9
9
|
* `sync-routes` precondition — else exit 1 with nothing written.
|
|
10
|
-
* 2. `writeLicenseKeyFiles` — `ailk.key
|
|
11
|
-
* gitignored; the same
|
|
10
|
+
* 2. `writeLicenseKeyFiles` — `ailk.key`, plus `apps/api/.env` only when
|
|
11
|
+
* `apps/api` already exists (#7779), both 0600 and gitignored; the same
|
|
12
|
+
* writer `--license` uses. The output names only the files written.
|
|
12
13
|
* 3. For each of `<dir>`, `<dir>/apps/api`, `<dir>/apps/web` holding
|
|
13
14
|
* `.vercel/project.json`, with that directory as `cwd`:
|
|
14
15
|
* a. print the linked `projectId` about to change (SEC-13);
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
import { spawnSync } from "node:child_process";
|
|
32
33
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
33
34
|
import { join, relative, resolve } from "node:path";
|
|
34
|
-
import {
|
|
35
|
+
import { LICENSE_ENV_VAR, LICENSE_KEY_FILE, assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
35
36
|
export const LICENSE_SET_HELP = `create-ailk license set — put your licence key into an existing site.
|
|
36
37
|
|
|
37
38
|
Usage:
|
|
@@ -44,9 +45,9 @@ Arguments:
|
|
|
44
45
|
<target-dir> An already-scaffolded repo (contains
|
|
45
46
|
architecture.yaml). Defaults to the current dir.
|
|
46
47
|
|
|
47
|
-
Writes the key to ailk.key and apps/api/.env
|
|
48
|
-
site (or apps/api, or apps/web) is linked to
|
|
49
|
-
production AILK_LICENSE_KEY variable and redeploys.
|
|
48
|
+
Writes the key to ailk.key, and to apps/api/.env when the site has apps/api
|
|
49
|
+
(owner-only, gitignored). When the site (or apps/api, or apps/web) is linked to
|
|
50
|
+
Vercel, it also replaces the production AILK_LICENSE_KEY variable and redeploys.
|
|
50
51
|
`;
|
|
51
52
|
const LINK_DIRS = [".", join("apps", "api"), join("apps", "web")];
|
|
52
53
|
const VERCELIGNORE_LINES = [LICENSE_KEY_FILE, ".env"];
|
|
@@ -151,9 +152,13 @@ export function runLicenseSet(argv, io) {
|
|
|
151
152
|
if (!existsSync(join(target, "architecture.yaml"))) {
|
|
152
153
|
return fail(`not a scaffolded repo: no architecture.yaml at ${target}. Run it from your site's root, or pass the site's directory.`);
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
+
// A site without apps/api (a web-only spoke) keeps the key in ailk.key
|
|
156
|
+
// alone — writing apps/api/.env would create an app directory it never had.
|
|
157
|
+
const files = writeLicenseKeyFiles(target, key, {
|
|
158
|
+
apiEnv: existsSync(join(target, "apps", "api")),
|
|
159
|
+
});
|
|
155
160
|
const keyFile = join(target, LICENSE_KEY_FILE);
|
|
156
|
-
const written = `Wrote ${
|
|
161
|
+
const written = `Wrote ${files.map((f) => f.split("\\").join("/")).join(" and ")} in ${target}`;
|
|
157
162
|
io.stdout(`[create-ailk] ${written} (owner-only, gitignored).\n`);
|
|
158
163
|
const linked = LINK_DIRS.map((d) => join(target, d)).filter((d) => existsSync(join(d, ".vercel", "project.json")));
|
|
159
164
|
if (linked.length === 0) {
|