create-ailk 0.19.0 → 0.20.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 +64 -7
- package/dist/add-module.js +7 -2
- package/dist/cli.js +35 -0
- package/dist/copy.d.ts +41 -0
- package/dist/copy.js +87 -21
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/fetch-template-snapshot.d.ts +21 -0
- package/dist/lib/fetch-template-snapshot.js +205 -0
- package/dist/lib/merge-three-way.d.ts +31 -0
- package/dist/lib/merge-three-way.js +65 -0
- package/dist/lib/scaffold-render-sweep.d.ts +23 -0
- package/dist/lib/scaffold-render-sweep.js +52 -0
- package/dist/module-architecture.js +64 -5
- package/dist/programmatic.d.ts +7 -1
- package/dist/programmatic.js +40 -3
- package/dist/site-roster.js +21 -4
- package/dist/spoke-checkout.d.ts +7 -0
- package/dist/spoke-checkout.js +8 -1
- package/dist/surfaces.d.ts +14 -0
- package/dist/surfaces.js +98 -3
- package/dist/template-record.d.ts +59 -0
- package/dist/template-record.js +183 -0
- package/dist/topology.js +29 -1
- package/dist/update.d.ts +48 -0
- package/dist/update.js +504 -0
- package/package.json +3 -3
- package/templates/.claude/cloudparity-repo-hooks.json +9 -0
- package/templates/.claude/hooks/architecture-check.sh +8 -0
- package/templates/.claude/rules/loop-contract.md +129 -0
- package/templates/.claude/settings.json +3 -2
- package/templates/.env.example +13 -0
- package/templates/CLAUDE.md +1 -1
- package/templates/CONVENTIONS.md +3 -2
- package/templates/_gitignore +162 -0
- package/templates/apps/api/.env.example +17 -1
- package/templates/apps/api/CLAUDE.md +18 -8
- package/templates/apps/api/package.json +2 -1
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +41 -2
- package/templates/apps/api/src/__tests__/setup.ts +7 -0
- package/templates/apps/api/src/__tests__/stripe-boot.test.ts +132 -0
- package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +184 -0
- package/templates/apps/api/src/bin/rate-limit-probe.ts +53 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +8 -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__/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/proposal-paid-store.ts +49 -0
- package/templates/apps/api/src/lib/rate-limit-probe.ts +165 -0
- package/templates/apps/api/src/lib/rate-limit-store.ts +142 -0
- package/templates/apps/api/src/lib/stripe.ts +97 -5
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +13 -4
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +2 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +220 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +430 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +72 -14
- package/templates/apps/api/src/middleware/auth.ts +16 -4
- package/templates/apps/api/src/middleware/default-tenant.ts +9 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +295 -82
- 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 +15 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +5 -0
- package/templates/apps/api/src/routes/webhooks/README.md +33 -5
- 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-proposal-paid.test.ts +369 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +2 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +442 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +201 -6
- package/templates/apps/api/src/server.ts +125 -18
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +91 -0
- 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 +115 -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/audit-log.ts +72 -10
- package/templates/apps/api/src/services/consent.ts +488 -3
- package/templates/apps/api/src/services/default-tenant.ts +12 -3
- package/templates/apps/api/src/services/hub-sign-in.ts +2 -1
- package/templates/apps/api/src/services/lead-routing.ts +105 -12
- 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/public-site-resolver.ts +21 -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 +1 -1
- package/templates/apps/mcp/CLAUDE.md +19 -7
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +95 -8
- package/templates/apps/mcp/src/tools/person_notes.ts +25 -0
- package/templates/apps/mcp/src/tools/proposals.ts +27 -0
- package/templates/apps/mcp/src/workspace-server.ts +16 -0
- package/templates/apps/web/_gitignore +6 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +165 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +19 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +106 -23
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up.actions.ts +125 -0
- 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__/agreements-prompt.test.tsx +281 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +17 -3
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +154 -53
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +116 -27
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +57 -2
- 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 +124 -18
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +116 -18
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/onboarding.test.tsx +323 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/privacy.test.tsx +365 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +290 -20
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +56 -0
- package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +74 -4
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +8 -0
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-actions.ts +170 -0
- package/templates/apps/web/app/[locale]/(authed)/account/agreements-prompt.tsx +181 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +88 -3
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/pay-form.tsx +65 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +27 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +176 -3
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/pay.test.tsx +193 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +188 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +65 -4
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +17 -0
- package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +15 -9
- package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +3 -3
- package/templates/apps/web/app/[locale]/(authed)/account/general/contact-actions.ts +81 -0
- package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +70 -27
- package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +26 -3
- package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +65 -19
- package/templates/apps/web/app/[locale]/(authed)/account/onboarding/actions.ts +120 -0
- package/templates/apps/web/app/[locale]/(authed)/account/onboarding/onboarding-form.tsx +322 -0
- package/templates/apps/web/app/[locale]/(authed)/account/onboarding/page.tsx +126 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +43 -1
- package/templates/apps/web/app/[locale]/(authed)/account/privacy/actions.ts +65 -0
- package/templates/apps/web/app/[locale]/(authed)/account/privacy/page.tsx +226 -0
- package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +89 -37
- 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-frame.tsx +44 -34
- package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +1 -1
- package/templates/apps/web/app/[locale]/consent-banner.tsx +32 -0
- package/templates/apps/web/app/[locale]/consent.actions.ts +71 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +13 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +25 -3
- package/templates/apps/web/app/[locale]/layout.tsx +23 -2
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +12 -1
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe-rsc-boundary.test.tsx +209 -0
- package/templates/apps/web/app/[locale]/unsubscribe/__tests__/unsubscribe.test.tsx +158 -0
- package/templates/apps/web/app/[locale]/unsubscribe/page.tsx +54 -0
- package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe-button.tsx +73 -0
- package/templates/apps/web/app/[locale]/unsubscribe/unsubscribe.actions.ts +55 -0
- package/templates/apps/web/app/robots.txt/route.ts +13 -10
- package/templates/apps/web/auth.ts +20 -1
- package/templates/apps/web/jest.config.cjs +3 -0
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +84 -0
- package/templates/apps/web/lib/__tests__/consent-visitor.test.ts +163 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +49 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +96 -7
- package/templates/apps/web/lib/account-config.ts +31 -16
- package/templates/apps/web/lib/auth-email.ts +45 -10
- package/templates/apps/web/lib/consent-visitor.ts +103 -0
- package/templates/apps/web/lib/relay.ts +20 -0
- package/templates/apps/web/lib/topology.generated.ts +9 -0
- package/templates/apps/web/messages/en.json +121 -18
- package/templates/apps/web/next.config.mjs +8 -2
- package/templates/database/CHANGELOG.md +55 -0
- 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__/outbound-message-action.db.test.ts +80 -0
- package/templates/database/__tests__/person-contact.db.test.ts +563 -0
- package/templates/database/__tests__/person-notes.db.test.ts +249 -0
- package/templates/database/__tests__/person.db.test.ts +3 -1
- package/templates/database/__tests__/piece-kind-migration.test.ts +67 -0
- package/templates/database/__tests__/piece-kind.db.test.ts +208 -0
- package/templates/database/_gitignore +8 -0
- package/templates/database/inbox/schema.prisma +114 -22
- 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/migrations/20261015120000_person_notes/migration.sql +77 -0
- package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +43 -0
- package/templates/database/migrations/20261016120000_piece_kind/migration.sql +95 -0
- package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +30 -0
- package/templates/database/ops/schema.prisma +16 -0
- package/templates/database/package.json +1 -1
- package/templates/project.yaml +6 -3
- package/versions.json +35 -35
- package/templates/.claude/rules/preserve-existing-style.md +0 -51
package/dist/surfaces.js
CHANGED
|
@@ -94,6 +94,11 @@ export const CORE_ROUTE_DIRS = [
|
|
|
94
94
|
// 404s. Neither `GATEABLE_SURFACES` nor `MODULE_SURFACES.commerce` gains
|
|
95
95
|
// an entry — the claim decides whether the pages serve, not the scaffold.
|
|
96
96
|
`${ROUTE_ROOT}/purchase`,
|
|
97
|
+
// #8438 D17/D21 — the one unsubscribe landing every signed link calls.
|
|
98
|
+
// Consent logging is part of the never-gated privacy floor (CLAUDE.md §
|
|
99
|
+
// License & tier hygiene's never-gate list), so this ships unconditionally
|
|
100
|
+
// like `/about`/`/contact`/`/faq` above — never a `GATEABLE_SURFACES` entry.
|
|
101
|
+
`${ROUTE_ROOT}/unsubscribe`,
|
|
97
102
|
];
|
|
98
103
|
/**
|
|
99
104
|
* Directory names skipped by `listTemplateRouteDirs`'s walker even though they
|
|
@@ -503,6 +508,31 @@ export function excludedContentPaths(enabled) {
|
|
|
503
508
|
}
|
|
504
509
|
return out.sort();
|
|
505
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* True when `path` (scaffold-relative, `/`-separated) lies inside one of the
|
|
513
|
+
* content directories `excludedContentPaths(enabled)` names, with `<locale>`
|
|
514
|
+
* matching any one path segment (#8427 D4).
|
|
515
|
+
*
|
|
516
|
+
* This is `excludedContentPaths`' first non-export reader — its own comment
|
|
517
|
+
* already named exactly this class ("orphan content makes `/sitemap.xml`
|
|
518
|
+
* advertise URLs that 404"). It is wired into the `skip` predicate
|
|
519
|
+
* `extractModuleBundle` already takes (`programmatic.ts`, `add-module.ts`),
|
|
520
|
+
* and into `layerInstalledSeeds`'s installed-seed count (`site-roster.ts`),
|
|
521
|
+
* so a module bundle never writes — and a roster check never counts — a
|
|
522
|
+
* seed under a gateable surface the scaffold does not enable.
|
|
523
|
+
*/
|
|
524
|
+
export function isUnservedSurfaceContent(path, enabled) {
|
|
525
|
+
const segments = path.split("/");
|
|
526
|
+
for (const contentPath of excludedContentPaths([...enabled])) {
|
|
527
|
+
const prefixSegments = contentPath.split("/");
|
|
528
|
+
if (prefixSegments.length > segments.length)
|
|
529
|
+
continue;
|
|
530
|
+
const matches = prefixSegments.every((segment, i) => segment === "<locale>" || segment === segments[i]);
|
|
531
|
+
if (matches)
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
506
536
|
/**
|
|
507
537
|
* Route manifest (#4986): every template route directory, keyed relative to
|
|
508
538
|
* ROUTE_ROOT (`apps/web/app/[locale]`), → the `@working-theory/*` fixed-group
|
|
@@ -641,12 +671,20 @@ export const ROUTE_SINCE = {
|
|
|
641
671
|
// version, the same rule #7430/#8232 applied.
|
|
642
672
|
"(authed)/account/security": "0.38.0",
|
|
643
673
|
"(authed)/account/notifications": "0.38.0",
|
|
674
|
+
// #8438 D21 — User settings' fifth tab, Privacy, and the public
|
|
675
|
+
// unsubscribe landing every signed link calls. Genuinely new, so they
|
|
676
|
+
// attach at today's fixed-group version, the same rule #8339 applied.
|
|
677
|
+
"(authed)/account/privacy": "0.40.0",
|
|
678
|
+
unsubscribe: "0.40.0",
|
|
644
679
|
// #8234 — the member Artifacts pages and a version's file route. Genuinely
|
|
645
680
|
// new, so they attach at today's fixed-group version, the same rule #7430
|
|
646
681
|
// applied.
|
|
647
682
|
"(authed)/account/artifacts": "0.38.0",
|
|
648
683
|
"(authed)/account/artifacts/[pieceId]": "0.38.0",
|
|
649
684
|
"(authed)/account/artifacts/[pieceId]/versions/[version]/file": "0.38.0",
|
|
685
|
+
// #8515 — the client's onboarding page. Genuinely new, so it attaches at
|
|
686
|
+
// today's fixed-group version, the same rule #7430 applied.
|
|
687
|
+
"(authed)/account/onboarding": "0.40.0",
|
|
650
688
|
// The leads inbox (#5678). The ROUTE ships in every scaffold, Free
|
|
651
689
|
// included — lead capture is a never-gate surface, so reading a lead back
|
|
652
690
|
// is one too. Whether it shows a ROW is a separate question with two
|
|
@@ -749,7 +787,13 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
749
787
|
"apps/web/lib/auth-routes.ts",
|
|
750
788
|
],
|
|
751
789
|
"(auth)/sign-in": ["apps/web/lib/auth-env.ts"],
|
|
752
|
-
|
|
790
|
+
// #8438 D13/D21 — the sign-up card's agreements gate also carries the
|
|
791
|
+
// carried-cookie mint, `sign-up.actions.ts`, beside it in this route's own
|
|
792
|
+
// directory.
|
|
793
|
+
"(auth)/sign-up": [
|
|
794
|
+
"apps/web/lib/auth-env.ts",
|
|
795
|
+
`${ROUTE_ROOT}/(auth)/sign-up/sign-up.actions.ts`,
|
|
796
|
+
],
|
|
753
797
|
"(auth)/forgot-password": ["apps/web/lib/auth-env.ts"],
|
|
754
798
|
"(auth)/reset-password": ["apps/web/lib/auth-env.ts"],
|
|
755
799
|
// #7430 — the group's layout reads `isAccountPath()` to give the account
|
|
@@ -764,6 +808,17 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
764
808
|
"apps/web/lib/server-api.ts",
|
|
765
809
|
"apps/web/lib/site-brand.tsx",
|
|
766
810
|
"apps/web/lib/site-logo.tsx",
|
|
811
|
+
// #8438 D13/D21 — two brand-new files in this already-existing route
|
|
812
|
+
// directory; a scaffold whose `(authed)/account` predates this unit
|
|
813
|
+
// needs them added, which the directory-level sync alone would not do
|
|
814
|
+
// (sync never overwrites or adds to a directory it already has).
|
|
815
|
+
// `acceptCarriedAgreementsAction` (the carried-cookie CONSUME side) lives
|
|
816
|
+
// in this directory's own `agreements-actions.ts`, never in
|
|
817
|
+
// `(auth)/sign-up` — see that file's header: `(auth)/**` is a sign-in
|
|
818
|
+
// seed a sign-in-less spoke drops, so a cross-directory reach from here
|
|
819
|
+
// into it would dangle.
|
|
820
|
+
`${ROUTE_ROOT}/(authed)/account/agreements-prompt.tsx`,
|
|
821
|
+
`${ROUTE_ROOT}/(authed)/account/agreements-actions.ts`,
|
|
767
822
|
],
|
|
768
823
|
// #7430 — the three child routes also import files in their PARENT's
|
|
769
824
|
// directory. A scaffold whose `(authed)/account` predates #7430 keeps its
|
|
@@ -834,23 +889,47 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
834
889
|
`${ROUTE_ROOT}/(authed)/account/components/send-message.tsx`,
|
|
835
890
|
],
|
|
836
891
|
// #8339 D11 — Security holds the former Sign-in section, moved verbatim
|
|
837
|
-
// from General: `sign-in-method.ts` and what it imports.
|
|
892
|
+
// from General: `sign-in-method.ts` and what it imports. #8431 D8 — the
|
|
893
|
+
// tab's failed-read row (`error-row.tsx` and its `retry-button.tsx`); its
|
|
894
|
+
// own `security-source.ts` and `security-panel.tsx` live in the route dir
|
|
895
|
+
// and copy with it.
|
|
838
896
|
"(authed)/account/security": [
|
|
839
897
|
"apps/web/lib/account-config.ts",
|
|
840
898
|
"apps/web/lib/auth-env.ts",
|
|
841
899
|
"apps/web/lib/server-api.ts",
|
|
842
900
|
"apps/web/lib/hub-session/config.ts",
|
|
843
901
|
`${ROUTE_ROOT}/(authed)/account/account-data.ts`,
|
|
902
|
+
`${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
|
|
903
|
+
`${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
|
|
844
904
|
`${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
|
|
845
905
|
`${ROUTE_ROOT}/(authed)/account/sign-in-method.ts`,
|
|
846
906
|
],
|
|
847
907
|
// #8339 D11 — Notifications holds the former Privacy section's
|
|
848
|
-
// cookie-settings link, moved verbatim from General.
|
|
908
|
+
// cookie-settings link, moved verbatim from General. #8438 D15 — that
|
|
909
|
+
// link moved on again, to Privacy; Notifications now reads the registry
|
|
910
|
+
// through `member.consents` instead, so it keeps the same dependency set.
|
|
849
911
|
"(authed)/account/notifications": [
|
|
850
912
|
"apps/web/lib/account-config.ts",
|
|
851
913
|
"apps/web/lib/server-api.ts",
|
|
852
914
|
`${ROUTE_ROOT}/(authed)/account/account-data.ts`,
|
|
853
915
|
`${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
|
|
916
|
+
`${ROUTE_ROOT}/(authed)/account/privacy/actions.ts`,
|
|
917
|
+
],
|
|
918
|
+
// #8438 D14 — Privacy's five sections: the summary, the cookie-choice
|
|
919
|
+
// read from the browser cookie, data-use consents and read-only
|
|
920
|
+
// agreements (both through `member.consents`), and Request account
|
|
921
|
+
// deletion, moved here unchanged from General (D16) — so Privacy needs
|
|
922
|
+
// General's own deletion files too, the same cross-directory rule #8339
|
|
923
|
+
// D11 applied to Notifications' cookie-settings link.
|
|
924
|
+
"(authed)/account/privacy": [
|
|
925
|
+
"apps/web/lib/account-config.ts",
|
|
926
|
+
"apps/web/lib/server-api.ts",
|
|
927
|
+
`${ROUTE_ROOT}/(authed)/account/account-data.ts`,
|
|
928
|
+
`${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
|
|
929
|
+
`${ROUTE_ROOT}/(authed)/account/sign-in-method.ts`,
|
|
930
|
+
`${ROUTE_ROOT}/(authed)/account/profile-actions.ts`,
|
|
931
|
+
`${ROUTE_ROOT}/(authed)/account/general/deletion-request.tsx`,
|
|
932
|
+
`${ROUTE_ROOT}/(authed)/account/general/profile-source.ts`,
|
|
854
933
|
],
|
|
855
934
|
"(authed)/account/billing": [
|
|
856
935
|
"apps/web/lib/account-config.ts",
|
|
@@ -894,6 +973,17 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
894
973
|
`${ROUTE_ROOT}/(authed)/account/artifacts/artifact-actions.ts`,
|
|
895
974
|
],
|
|
896
975
|
"(authed)/account/artifacts/[pieceId]/versions/[version]/file": ["apps/web/lib/server-api.ts"],
|
|
976
|
+
// #8515 D12 — the onboarding page imports its parent's account-data.ts
|
|
977
|
+
// and error-row.tsx (which renders retry-button.tsx); its actions forward
|
|
978
|
+
// the credential `forwardedCookieInit` returns. account-data.ts reads the
|
|
979
|
+
// account config, as the Artifacts pages' does.
|
|
980
|
+
"(authed)/account/onboarding": [
|
|
981
|
+
"apps/web/lib/account-config.ts",
|
|
982
|
+
"apps/web/lib/server-api.ts",
|
|
983
|
+
`${ROUTE_ROOT}/(authed)/account/account-data.ts`,
|
|
984
|
+
`${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
|
|
985
|
+
`${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
|
|
986
|
+
],
|
|
897
987
|
"(authed)/account/support": [
|
|
898
988
|
"apps/web/lib/account-config.ts",
|
|
899
989
|
"apps/web/lib/server-api.ts",
|
|
@@ -967,10 +1057,13 @@ export const ROUTE_MESSAGE_NAMESPACES = {
|
|
|
967
1057
|
"(authed)/account/general": ["account"],
|
|
968
1058
|
"(authed)/account/security": ["account"],
|
|
969
1059
|
"(authed)/account/notifications": ["account"],
|
|
1060
|
+
"(authed)/account/privacy": ["account"],
|
|
970
1061
|
"(authed)/account/billing": ["account"],
|
|
971
1062
|
// #8234 — the two Artifacts pages; the file route renders no string.
|
|
972
1063
|
"(authed)/account/artifacts": ["account"],
|
|
973
1064
|
"(authed)/account/artifacts/[pieceId]": ["account"],
|
|
1065
|
+
// #8515 — the onboarding page.
|
|
1066
|
+
"(authed)/account/onboarding": ["account"],
|
|
974
1067
|
"(authed)/admin": ["admin"],
|
|
975
1068
|
"(authed)/leads": ["leadsInbox"],
|
|
976
1069
|
// #5977 — every string on both editor routes comes from the `edit`
|
|
@@ -982,6 +1075,8 @@ export const ROUTE_MESSAGE_NAMESPACES = {
|
|
|
982
1075
|
"(authed)/waitlist": ["waitlistDashboard.gate"],
|
|
983
1076
|
// #7744 — the /team hub's empty state reads the `team` namespace.
|
|
984
1077
|
team: ["team"],
|
|
1078
|
+
// #8438 D21 — the unsubscribe landing's own namespace.
|
|
1079
|
+
unsubscribe: ["unsubscribe"],
|
|
985
1080
|
};
|
|
986
1081
|
/** Template-relative path of the generated site-nav registry. */
|
|
987
1082
|
export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Where the per-file record lives, beside `.ailk-site-manifest.json`. */
|
|
2
|
+
export declare const TEMPLATE_RECORD_PATH = ".ailk-template-files.json";
|
|
3
|
+
/**
|
|
4
|
+
* The app paths a generator rewrites after `copyTemplate` lands its copy
|
|
5
|
+
* (§1): the nav, the site chrome, the site fonts, the paid-sections
|
|
6
|
+
* registry, and the four spoke seams. Composed from each writer's own
|
|
7
|
+
* exported path constant, so a renamed constant carries its path along
|
|
8
|
+
* here too (#8522 D2).
|
|
9
|
+
*/
|
|
10
|
+
export declare const REGENERATED_APP_PATHS: readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* Whether a destination path (`/`-separated, as `templateDestination`
|
|
13
|
+
* returns it) is one `update` ever records or touches (#8522 D2): under one
|
|
14
|
+
* of the three app trees, not a `package.json`, not an `.env*` file, no
|
|
15
|
+
* `.claude` segment, and not a path a generator rewrites after the copy.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isRecordable(destRel: string): boolean;
|
|
18
|
+
/** The hex sha256 digest of `bytes` — the one hash every record row uses. */
|
|
19
|
+
export declare function sha256Hex(bytes: Uint8Array): string;
|
|
20
|
+
export interface TemplateFileRow {
|
|
21
|
+
sha256: string;
|
|
22
|
+
version: string;
|
|
23
|
+
}
|
|
24
|
+
export interface TemplateRecord {
|
|
25
|
+
schema: 1;
|
|
26
|
+
projectName: string;
|
|
27
|
+
files: Record<string, TemplateFileRow>;
|
|
28
|
+
}
|
|
29
|
+
/** Thrown by `parseTemplateRecord` on a malformed or unsafe record. */
|
|
30
|
+
export declare class TemplateRecordError extends Error {
|
|
31
|
+
constructor(message: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse `.ailk-template-files.json` text, refusing — naming the field — on:
|
|
35
|
+
* JSON that does not parse; `schema !== 1`; an unknown top-level key; an
|
|
36
|
+
* empty `projectName`; a `files` key that is not a recordable path; a
|
|
37
|
+
* `sha256` that is not 64 lowercase hex; a `version` that is not a semver
|
|
38
|
+
* string (#8522 D1).
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseTemplateRecord(text: string): TemplateRecord;
|
|
41
|
+
/** `record` as `scaffold()` and `update` write it: sorted keys, trailing newline. */
|
|
42
|
+
export declare function serializeTemplateRecord(record: TemplateRecord): string;
|
|
43
|
+
export interface PlanTemplateRecordOptions {
|
|
44
|
+
/** The freshly scaffolded site. */
|
|
45
|
+
targetDir: string;
|
|
46
|
+
/** Destination path -> sha256 of the bytes `copyTemplate`'s `onWrite` wrote, for every recordable path. */
|
|
47
|
+
copied: ReadonlyMap<string, string>;
|
|
48
|
+
/** The create-ailk version that wrote this scaffold. */
|
|
49
|
+
version: string;
|
|
50
|
+
projectName: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The record a fresh scaffold writes (#8522 D2): one row per copied path
|
|
54
|
+
* whose file still exists and whose current bytes still hash to the copy —
|
|
55
|
+
* a later layer (a module bundle's patch, a generator) that rewrote the
|
|
56
|
+
* file drops it from the record, exactly as it is absent from the record a
|
|
57
|
+
* `create-ailk update` keeps current for it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function planTemplateRecord(opts: PlanTemplateRecordOptions): TemplateRecord;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.ailk-template-files.json` — the per-file record of what a scaffold's
|
|
3
|
+
* copied app code is (#8522 D1/D2).
|
|
4
|
+
*
|
|
5
|
+
* Written beside `.ailk-site-manifest.json` at the site root, it lists every
|
|
6
|
+
* `apps/web/`, `apps/api/` and `apps/mcp/` file the base `templates/` copy
|
|
7
|
+
* wrote and no later layer rewrote, with the sha256 of the template's copy
|
|
8
|
+
* of that file and the create-ailk version it came from. `create-ailk
|
|
9
|
+
* update` (`update.ts`) reads it to tell an untouched copy from an owner's
|
|
10
|
+
* edit; `scaffold()` (`programmatic.ts`) writes it, as its last write, from
|
|
11
|
+
* the `onWrite` hook `copyTemplate` (`copy.ts`) calls once per file.
|
|
12
|
+
*
|
|
13
|
+
* Pure module: no `import.meta.url`, no filesystem write — `planTemplateRecord`
|
|
14
|
+
* only READS the scaffold's own disk to confirm a copied path's final bytes
|
|
15
|
+
* (a later layer may have rewritten it, D2); every write goes through the
|
|
16
|
+
* caller.
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { PAID_SECTIONS_PATH } from "./paid-sections.js";
|
|
22
|
+
import { SITE_CHROME_PATH } from "./site-chrome.js";
|
|
23
|
+
import { SITE_FONTS_PATH } from "./site-fonts.js";
|
|
24
|
+
import { NAV_CONFIG_PATH } from "./surfaces.js";
|
|
25
|
+
import { SPOKE_SEAM_FILES } from "./topology.js";
|
|
26
|
+
/** Where the per-file record lives, beside `.ailk-site-manifest.json`. */
|
|
27
|
+
export const TEMPLATE_RECORD_PATH = ".ailk-template-files.json";
|
|
28
|
+
/**
|
|
29
|
+
* The app paths a generator rewrites after `copyTemplate` lands its copy
|
|
30
|
+
* (§1): the nav, the site chrome, the site fonts, the paid-sections
|
|
31
|
+
* registry, and the four spoke seams. Composed from each writer's own
|
|
32
|
+
* exported path constant, so a renamed constant carries its path along
|
|
33
|
+
* here too (#8522 D2).
|
|
34
|
+
*/
|
|
35
|
+
export const REGENERATED_APP_PATHS = [
|
|
36
|
+
NAV_CONFIG_PATH,
|
|
37
|
+
SITE_CHROME_PATH,
|
|
38
|
+
SITE_FONTS_PATH,
|
|
39
|
+
PAID_SECTIONS_PATH,
|
|
40
|
+
...SPOKE_SEAM_FILES,
|
|
41
|
+
];
|
|
42
|
+
const REGENERATED_SET = new Set(REGENERATED_APP_PATHS);
|
|
43
|
+
const RECORDABLE_APP_ROOT = /^apps\/(web|api|mcp)\//;
|
|
44
|
+
/**
|
|
45
|
+
* Whether a destination path (`/`-separated, as `templateDestination`
|
|
46
|
+
* returns it) is one `update` ever records or touches (#8522 D2): under one
|
|
47
|
+
* of the three app trees, not a `package.json`, not an `.env*` file, no
|
|
48
|
+
* `.claude` segment, and not a path a generator rewrites after the copy.
|
|
49
|
+
*/
|
|
50
|
+
export function isRecordable(destRel) {
|
|
51
|
+
if (!RECORDABLE_APP_ROOT.test(destRel))
|
|
52
|
+
return false;
|
|
53
|
+
const segments = destRel.split("/");
|
|
54
|
+
const base = segments[segments.length - 1] ?? "";
|
|
55
|
+
if (base === "package.json")
|
|
56
|
+
return false;
|
|
57
|
+
if (base.startsWith(".env"))
|
|
58
|
+
return false;
|
|
59
|
+
if (segments.includes(".claude"))
|
|
60
|
+
return false;
|
|
61
|
+
if (REGENERATED_SET.has(destRel))
|
|
62
|
+
return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
/** The hex sha256 digest of `bytes` — the one hash every record row uses. */
|
|
66
|
+
export function sha256Hex(bytes) {
|
|
67
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
68
|
+
}
|
|
69
|
+
/** Thrown by `parseTemplateRecord` on a malformed or unsafe record. */
|
|
70
|
+
export class TemplateRecordError extends Error {
|
|
71
|
+
constructor(message) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = "TemplateRecordError";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const VERSION_PATTERN = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
|
|
77
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
78
|
+
/**
|
|
79
|
+
* Whether `key` is traversal-safe: no `..`, no empty segment, no backslash,
|
|
80
|
+
* no leading `/` (D1). This alone does not make `key` a path `update` may
|
|
81
|
+
* touch — `parseTemplateRecord` also requires `isRecordable(key)`, so a
|
|
82
|
+
* tampered record can never direct a write or delete outside the three app
|
|
83
|
+
* trees nor at a non-recordable path (`package.json`, `.env*`, a
|
|
84
|
+
* `REGENERATED_APP_PATHS` entry, …) inside them.
|
|
85
|
+
*/
|
|
86
|
+
function isSafeRecordKey(key) {
|
|
87
|
+
if (key.length === 0 || key.startsWith("/") || key.includes("\\")) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const segments = key.split("/");
|
|
91
|
+
return segments.every((segment) => segment !== "" && segment !== "." && segment !== "..");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Parse `.ailk-template-files.json` text, refusing — naming the field — on:
|
|
95
|
+
* JSON that does not parse; `schema !== 1`; an unknown top-level key; an
|
|
96
|
+
* empty `projectName`; a `files` key that is not a recordable path; a
|
|
97
|
+
* `sha256` that is not 64 lowercase hex; a `version` that is not a semver
|
|
98
|
+
* string (#8522 D1).
|
|
99
|
+
*/
|
|
100
|
+
export function parseTemplateRecord(text) {
|
|
101
|
+
let doc;
|
|
102
|
+
try {
|
|
103
|
+
doc = JSON.parse(text);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} is not valid JSON: ${err.message}`);
|
|
107
|
+
}
|
|
108
|
+
if (typeof doc !== "object" || doc === null || Array.isArray(doc)) {
|
|
109
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} is not a JSON object`);
|
|
110
|
+
}
|
|
111
|
+
const record = doc;
|
|
112
|
+
const allowed = new Set(["schema", "projectName", "files"]);
|
|
113
|
+
for (const key of Object.keys(record)) {
|
|
114
|
+
if (!allowed.has(key)) {
|
|
115
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} has an unknown top-level key "${key}"`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (record["schema"] !== 1) {
|
|
119
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "schema" must be 1, got ${JSON.stringify(record["schema"])}`);
|
|
120
|
+
}
|
|
121
|
+
const projectName = record["projectName"];
|
|
122
|
+
if (typeof projectName !== "string" || projectName.length === 0) {
|
|
123
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "projectName" must be a non-empty string`);
|
|
124
|
+
}
|
|
125
|
+
const filesRaw = record["files"];
|
|
126
|
+
if (typeof filesRaw !== "object" || filesRaw === null || Array.isArray(filesRaw)) {
|
|
127
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files" must be an object`);
|
|
128
|
+
}
|
|
129
|
+
const files = {};
|
|
130
|
+
for (const [key, value] of Object.entries(filesRaw)) {
|
|
131
|
+
if (!isSafeRecordKey(key)) {
|
|
132
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files" has an unsafe key "${key}"`);
|
|
133
|
+
}
|
|
134
|
+
if (!isRecordable(key)) {
|
|
135
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files" has a key "${key}" that is not a recordable path`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof value !== "object" || value === null) {
|
|
138
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files"["${key}"] must be an object`);
|
|
139
|
+
}
|
|
140
|
+
const row = value;
|
|
141
|
+
const sha = row["sha256"];
|
|
142
|
+
if (typeof sha !== "string" || !SHA256_PATTERN.test(sha)) {
|
|
143
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files"["${key}"].sha256 must be 64 lowercase hex characters`);
|
|
144
|
+
}
|
|
145
|
+
const version = row["version"];
|
|
146
|
+
if (typeof version !== "string" || !VERSION_PATTERN.test(version)) {
|
|
147
|
+
throw new TemplateRecordError(`${TEMPLATE_RECORD_PATH} "files"["${key}"].version must be a semver string`);
|
|
148
|
+
}
|
|
149
|
+
files[key] = { sha256: sha, version };
|
|
150
|
+
}
|
|
151
|
+
return { schema: 1, projectName, files };
|
|
152
|
+
}
|
|
153
|
+
/** `record` as `scaffold()` and `update` write it: sorted keys, trailing newline. */
|
|
154
|
+
export function serializeTemplateRecord(record) {
|
|
155
|
+
const files = {};
|
|
156
|
+
for (const key of Object.keys(record.files).sort()) {
|
|
157
|
+
files[key] = record.files[key];
|
|
158
|
+
}
|
|
159
|
+
return (JSON.stringify({ schema: record.schema, projectName: record.projectName, files }, null, 2) +
|
|
160
|
+
"\n");
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The record a fresh scaffold writes (#8522 D2): one row per copied path
|
|
164
|
+
* whose file still exists and whose current bytes still hash to the copy —
|
|
165
|
+
* a later layer (a module bundle's patch, a generator) that rewrote the
|
|
166
|
+
* file drops it from the record, exactly as it is absent from the record a
|
|
167
|
+
* `create-ailk update` keeps current for it.
|
|
168
|
+
*/
|
|
169
|
+
export function planTemplateRecord(opts) {
|
|
170
|
+
const files = {};
|
|
171
|
+
for (const path of [...opts.copied.keys()].sort()) {
|
|
172
|
+
const hash = opts.copied.get(path);
|
|
173
|
+
if (hash === undefined)
|
|
174
|
+
continue;
|
|
175
|
+
const abs = join(opts.targetDir, ...path.split("/"));
|
|
176
|
+
if (!existsSync(abs))
|
|
177
|
+
continue;
|
|
178
|
+
if (sha256Hex(readFileSync(abs)) !== hash)
|
|
179
|
+
continue;
|
|
180
|
+
files[path] = { sha256: hash, version: opts.version };
|
|
181
|
+
}
|
|
182
|
+
return { schema: 1, projectName: opts.projectName, files };
|
|
183
|
+
}
|
package/dist/topology.js
CHANGED
|
@@ -273,7 +273,9 @@ const WEB_SEAM_HEADER = `// topology.generated.ts — how this site reads a sess
|
|
|
273
273
|
// UNSERVED_MCP_TOOLS names the MCP tools /.well-known/mcp.json leaves out
|
|
274
274
|
// on this topology (#8070).
|
|
275
275
|
// loadSpokeBilling reaches the spoke billing action only on a spoke that
|
|
276
|
-
// signs in through its hub and carries commerce (#8217); elsewhere, null
|
|
276
|
+
// signs in through its hub and carries commerce (#8217); elsewhere, null.
|
|
277
|
+
// readOwnAccounts lists the signed-in person's own sign-in accounts through
|
|
278
|
+
// Better Auth on a hub (#8431); a spoke has none here, so it answers null.`;
|
|
277
279
|
const MCP_SEAM_HEADER = `// topology.generated.ts — how this MCP server resolves its workspace store.
|
|
278
280
|
//
|
|
279
281
|
// GENERATED by create-ailk for this site's topology (#7426). Do not edit:
|
|
@@ -303,6 +305,26 @@ const SITE_SESSION_TYPE = `export type SiteSession = {
|
|
|
303
305
|
createdAt?: Date | string | null;
|
|
304
306
|
};
|
|
305
307
|
} | null;`;
|
|
308
|
+
/**
|
|
309
|
+
* `readOwnAccounts` (#8431 D2a): the session's own sign-in accounts, which
|
|
310
|
+
* the account Security tab reads to say whether the person has a password.
|
|
311
|
+
* The hub render loads `auth.ts` only when it is called; a spoke has no local
|
|
312
|
+
* accounts, so its renders answer `null` and import nothing.
|
|
313
|
+
*/
|
|
314
|
+
function ownAccountsReader(topology) {
|
|
315
|
+
if (topology === "hub") {
|
|
316
|
+
return `export async function readOwnAccounts(
|
|
317
|
+
headers: Headers,
|
|
318
|
+
): Promise<ReadonlyArray<{ providerId: string }> | null> {
|
|
319
|
+
const { auth } = await import("../auth");
|
|
320
|
+
return auth.api.listUserAccounts({ headers });
|
|
321
|
+
}`;
|
|
322
|
+
}
|
|
323
|
+
return `export function readOwnAccounts(headers: Headers): Promise<ReadonlyArray<{ providerId: string }> | null> {
|
|
324
|
+
void headers;
|
|
325
|
+
return Promise.resolve(null);
|
|
326
|
+
}`;
|
|
327
|
+
}
|
|
306
328
|
/** The sign-in exports every web seam render carries (#7894 D8). */
|
|
307
329
|
function signInExports(values) {
|
|
308
330
|
const literal = (value) => value === null ? "null" : JSON.stringify(value);
|
|
@@ -382,6 +404,8 @@ export async function readSession(headers: Headers): Promise<SiteSession> {
|
|
|
382
404
|
return auth.api.getSession({ headers });
|
|
383
405
|
}
|
|
384
406
|
|
|
407
|
+
${ownAccountsReader("hub")}
|
|
408
|
+
|
|
385
409
|
export async function registerSiteStores(): Promise<void> {
|
|
386
410
|
const { registerLicenseKeyStore } = await import("./license-key-store");
|
|
387
411
|
await registerLicenseKeyStore();
|
|
@@ -404,6 +428,8 @@ export function readSession(headers: Headers): Promise<SiteSession> {
|
|
|
404
428
|
return readHubSession(headers);
|
|
405
429
|
}
|
|
406
430
|
|
|
431
|
+
${ownAccountsReader("spoke")}
|
|
432
|
+
|
|
407
433
|
export function registerSiteStores(): Promise<void> {
|
|
408
434
|
return Promise.resolve();
|
|
409
435
|
}
|
|
@@ -424,6 +450,8 @@ export function readSession(headers: Headers): Promise<SiteSession> {
|
|
|
424
450
|
return Promise.resolve(null);
|
|
425
451
|
}
|
|
426
452
|
|
|
453
|
+
${ownAccountsReader("spoke")}
|
|
454
|
+
|
|
427
455
|
export function registerSiteStores(): Promise<void> {
|
|
428
456
|
return Promise.resolve();
|
|
429
457
|
}
|
package/dist/update.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** A refusal the CLI exits 2 on, thrown before anything is written. */
|
|
2
|
+
export declare class UpdateRefusedError extends Error {
|
|
3
|
+
readonly exitCode = 2;
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export interface UpdateSiteOptions {
|
|
7
|
+
/** The existing site. */
|
|
8
|
+
targetDir: string;
|
|
9
|
+
/** The running package's own `templates/` directory. */
|
|
10
|
+
templatesDir: string;
|
|
11
|
+
/** The running create-ailk version. */
|
|
12
|
+
version: string;
|
|
13
|
+
/** Overrides `AILK_NPM_REGISTRY_URL` / the public npm registry default. */
|
|
14
|
+
registryUrl?: string;
|
|
15
|
+
/** Injected fetch for tests. Never a live network call in CI. */
|
|
16
|
+
fetchImpl?: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
export interface UpdateResult {
|
|
19
|
+
targetDir: string;
|
|
20
|
+
version: string;
|
|
21
|
+
updated: string[];
|
|
22
|
+
merged: string[];
|
|
23
|
+
added: string[];
|
|
24
|
+
removed: string[];
|
|
25
|
+
current: string[];
|
|
26
|
+
review: Array<{
|
|
27
|
+
path: string;
|
|
28
|
+
reason: string;
|
|
29
|
+
}>;
|
|
30
|
+
skipped: Array<{
|
|
31
|
+
path: string;
|
|
32
|
+
reason: string;
|
|
33
|
+
}>;
|
|
34
|
+
backfilled?: {
|
|
35
|
+
version: string;
|
|
36
|
+
files: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare function updateSite(opts: UpdateSiteOptions): Promise<UpdateResult>;
|
|
40
|
+
/** The printed report (D9): one section per non-empty list, in order. */
|
|
41
|
+
export declare function formatUpdateReport(result: UpdateResult): string;
|
|
42
|
+
export interface UpdateArgs {
|
|
43
|
+
target: string | undefined;
|
|
44
|
+
help: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare const UPDATE_HELP = "create-ailk update \u2014 bring template fixes into a site's copied app code.\n\nUsage:\n create-ailk update [<target-dir>]\n\nArguments:\n <target-dir> The existing site. Defaults to the current dir.\n\nOptions:\n --help, -h Print this message.\n\nReads .ailk-template-files.json (written beside .ailk-site-manifest.json by\nevery template scaffold) to tell an untouched copy from an edit. An\nuntouched apps/web, apps/api or apps/mcp file whose template copy changed is\noverwritten. An edited file whose template copy also changed is three-way\nmerged with `git` (git must be on PATH); a conflict leaves the file alone\nand is listed under \"Needs review\" \u2014 nothing else in the run is held back.\nA site with no record gets one on its first run, from the version its\n.ailk-site-manifest.json already records. Commit before running, and review\nthe diff after.\n";
|
|
47
|
+
/** Parse the argv AFTER the `update` word. Strict `node:util` parseArgs, like `parseAdoptArgs`. */
|
|
48
|
+
export declare function parseUpdateArgs(argv: string[]): UpdateArgs;
|