create-ailk 0.14.0 → 0.16.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 +124 -0
- package/component-catalog.json +3 -3
- package/dist/add-module.d.ts +7 -0
- package/dist/add-module.js +80 -3
- package/dist/add-route.js +26 -2
- package/dist/adopt.d.ts +74 -0
- package/dist/adopt.js +314 -0
- package/dist/apply-design.d.ts +12 -1
- package/dist/apply-design.js +36 -4
- package/dist/cli.js +103 -3
- package/dist/derive-architecture.d.ts +37 -2
- package/dist/derive-architecture.js +92 -7
- package/dist/design-package.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/chrome-readers.d.ts +21 -0
- package/dist/lib/chrome-readers.js +100 -0
- package/dist/lib/declared-env.d.ts +142 -0
- package/dist/lib/declared-env.js +985 -0
- package/dist/lib/env-catalog.d.ts +17 -0
- package/dist/lib/env-catalog.js +234 -0
- package/dist/lib/extract-module-bundle.d.ts +20 -0
- package/dist/lib/extract-module-bundle.js +53 -6
- package/dist/lib/license-key-files.d.ts +6 -1
- package/dist/lib/license-key-files.js +9 -4
- package/dist/lib/module-tier.d.ts +2 -0
- package/dist/lib/module-tier.js +4 -0
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +82 -0
- package/dist/parse-args.d.ts +27 -0
- package/dist/parse-args.js +89 -0
- package/dist/programmatic.d.ts +28 -0
- package/dist/programmatic.js +181 -11
- package/dist/seed-flows.d.ts +65 -0
- package/dist/seed-flows.js +350 -0
- package/dist/site-chrome.d.ts +4 -0
- package/dist/site-chrome.js +34 -3
- package/dist/site-manifest.d.ts +55 -5
- package/dist/site-manifest.js +121 -12
- package/dist/site-roster.d.ts +13 -0
- package/dist/site-roster.js +22 -8
- package/dist/spoke-checkout.d.ts +42 -0
- package/dist/spoke-checkout.js +359 -0
- package/dist/surfaces.js +7 -0
- package/dist/sync-routes.js +29 -5
- package/dist/topology.d.ts +246 -0
- package/dist/topology.js +942 -0
- package/package.json +3 -3
- package/site-templates/designs/minimalism/DESIGN.md +3 -3
- package/templates/.env.example +10 -0
- package/templates/apps/api/CLAUDE.md +21 -4
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +5 -1
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +95 -16
- package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +122 -0
- package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +362 -0
- package/templates/apps/api/src/bin/spokes.ts +62 -1
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +75 -0
- package/templates/apps/api/src/config/index.ts +35 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +99 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +114 -1
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +66 -5
- package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +209 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +54 -0
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +20 -0
- package/templates/apps/api/src/lib/agent-store.ts +95 -3
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +24 -5
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +419 -0
- package/templates/apps/api/src/lib/site.ts +37 -0
- package/templates/apps/api/src/lib/spoke-store.ts +41 -3
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +4 -0
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +310 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +22 -0
- package/templates/apps/api/src/middleware/auth.ts +129 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +23 -0
- package/templates/apps/api/src/openapi/spec.ts +38 -2
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +483 -0
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +201 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +50 -2
- package/templates/apps/api/src/routes/checkout/sessions.ts +7 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +282 -0
- package/templates/apps/api/src/routes/leads/index.ts +69 -6
- package/templates/apps/api/src/routes/webhooks/README.md +39 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +105 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +366 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +133 -2
- package/templates/apps/api/src/server.ts +105 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +4 -4
- package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +854 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +492 -0
- package/templates/apps/api/src/services/__tests__/person.test.ts +553 -0
- package/templates/apps/api/src/services/default-tenant.ts +41 -7
- package/templates/apps/api/src/services/hub-sign-in.ts +471 -0
- package/templates/apps/api/src/services/lead-routing.ts +278 -30
- package/templates/apps/api/src/services/person.ts +541 -0
- package/templates/apps/api/src/services/public-site-resolver.ts +48 -0
- package/templates/apps/mcp/__tests__/index.test.ts +5 -5
- package/templates/apps/mcp/src/index.ts +6 -4
- package/templates/apps/mcp/src/lib/content-adapter.ts +2 -2
- package/templates/apps/mcp/src/lib/topology.generated.ts +24 -0
- package/templates/apps/web/.env.example +58 -5
- package/templates/apps/web/CLAUDE.md +1 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-in/next-path.ts +0 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +206 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +256 -3
- package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +5 -8
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +205 -0
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +98 -29
- package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +6 -3
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -101
- package/templates/apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx +90 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +66 -14
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +105 -11
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +12 -8
- package/templates/apps/web/app/[locale]/layout.tsx +83 -18
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +5 -2
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +295 -0
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +156 -0
- package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +341 -0
- package/templates/apps/web/app/api/hub-session/callback/route.ts +168 -0
- package/templates/apps/web/app/api/hub-session/sign-in/route.ts +76 -0
- package/templates/apps/web/app/api/hub-session/sign-out/route.ts +63 -0
- package/templates/apps/web/app/openapi.json/__tests__/route.test.ts +104 -1
- package/templates/apps/web/app/openapi.json/route.ts +72 -7
- package/templates/apps/web/auth.ts +29 -2
- package/templates/apps/web/components/sign-out-trigger.tsx +20 -1
- package/templates/apps/web/components/signed-in-nav.tsx +17 -3
- package/templates/apps/web/instrumentation.ts +7 -4
- package/templates/apps/web/lib/__tests__/account-config.test.ts +5 -2
- package/templates/apps/web/lib/__tests__/closing-footer-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/listings-config.test.ts +83 -0
- package/templates/apps/web/lib/__tests__/listings.test.tsx +49 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +49 -1
- package/templates/apps/web/lib/__tests__/relay.test.ts +317 -0
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +52 -0
- package/templates/apps/web/lib/account-config.ts +130 -1
- package/templates/apps/web/lib/api-origin.generated.ts +11 -0
- package/templates/apps/web/lib/closing-footer-path.ts +50 -0
- package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +262 -0
- package/templates/apps/web/lib/hub-session/config.ts +104 -0
- package/templates/apps/web/lib/hub-session/index.ts +23 -0
- package/templates/apps/web/lib/hub-session/seal.ts +82 -0
- package/templates/apps/web/lib/hub-session/session.ts +180 -0
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +79 -0
- package/templates/apps/web/lib/listings-config.ts +31 -0
- package/templates/apps/web/lib/listings.tsx +7 -3
- package/templates/apps/web/lib/meeting-slugs.ts +11 -1
- package/templates/apps/web/lib/next-path.ts +35 -0
- package/templates/apps/web/lib/relay.ts +209 -3
- package/templates/apps/web/lib/server-api.ts +11 -1
- package/templates/apps/web/lib/site-brand.tsx +22 -1
- package/templates/apps/web/lib/site-chrome.ts +3 -0
- package/templates/apps/web/lib/topology.generated.ts +33 -0
- package/templates/apps/web/messages/en.json +32 -3
- package/templates/apps/web/package.json +1 -1
- package/templates/database/CHANGELOG.md +51 -0
- package/templates/database/__tests__/agents.db.test.ts +85 -0
- package/templates/database/__tests__/fixtures/person-backfill.json +264 -0
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +92 -0
- package/templates/database/__tests__/member-area.db.test.ts +245 -0
- package/templates/database/__tests__/person.db.test.ts +584 -0
- package/templates/database/auth/schema.prisma +24 -0
- package/templates/database/config/schema.prisma +13 -0
- package/templates/database/inbox/schema.prisma +111 -0
- package/templates/database/jest.config.cjs +7 -11
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +35 -0
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +20 -0
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +33 -0
- package/templates/database/migrations/20261010120000_person/migration.sql +106 -0
- package/templates/database/migrations/20261010121000_person_backfill/migration.sql +132 -0
- package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +20 -0
- package/templates/database/migrations/20261011120000_member_area/migration.sql +158 -0
- package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +21 -0
- package/templates/database/package.json +1 -1
- package/templates/database/scripts/person-backfill.sql +132 -0
- package/templates/database/tenancy.ts +53 -0
- package/templates/tools/check-declared-env.ts +37 -0
- package/templates/tools/declared-env.ts +1163 -0
- package/versions.json +35 -34
package/dist/surfaces.js
CHANGED
|
@@ -626,6 +626,10 @@ export const ROUTE_SINCE = {
|
|
|
626
626
|
"(authed)/account/inbox/[kind]/[id]": "0.30.0",
|
|
627
627
|
"(authed)/account/settings": "0.30.0",
|
|
628
628
|
"(authed)/account/support": "0.30.0",
|
|
629
|
+
// #7920 — the member file read a message's file action opens. Genuinely
|
|
630
|
+
// new, so it attaches at today's fixed-group version, the same rule #7430
|
|
631
|
+
// applied above.
|
|
632
|
+
"(authed)/account/inbox/message/[id]/file": "0.37.0",
|
|
629
633
|
// The leads inbox (#5678). The ROUTE ships in every scaffold, Free
|
|
630
634
|
// included — lead capture is a never-gate surface, so reading a lead back
|
|
631
635
|
// is one too. Whether it shows a ROW is a separate question with two
|
|
@@ -756,6 +760,9 @@ export const ROUTE_DEPENDENCIES = {
|
|
|
756
760
|
`${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
|
|
757
761
|
`${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
|
|
758
762
|
],
|
|
763
|
+
// #7920 — the member file read re-serves the hub's bytes with the
|
|
764
|
+
// credential `forwardedCookieInit` returns.
|
|
765
|
+
"(authed)/account/inbox/message/[id]/file": ["apps/web/lib/server-api.ts"],
|
|
759
766
|
"(authed)/account/settings": [
|
|
760
767
|
"apps/web/lib/account-config.ts",
|
|
761
768
|
"apps/web/lib/auth-env.ts",
|
package/dist/sync-routes.js
CHANGED
|
@@ -31,8 +31,11 @@ import { tmpdir } from "node:os";
|
|
|
31
31
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
32
32
|
import { parseArgs } from "node:util";
|
|
33
33
|
import { copyTemplate } from "./copy.js";
|
|
34
|
+
import { appendEnvRows } from "./lib/declared-env.js";
|
|
35
|
+
import { ENV_CATALOG } from "./lib/env-catalog.js";
|
|
34
36
|
import { substituteMarkers } from "./substitute.js";
|
|
35
37
|
import { GATEABLE_SURFACES, NON_ROUTE_DIRS, ROUTE_DEPENDENCIES, ROUTE_MESSAGE_NAMESPACES, ROUTE_SINCE, surfaceForRouteDir, } from "./surfaces.js";
|
|
38
|
+
import { SPOKE_SEAM_FILES, isSpokeExcluded, readSpokeShape, } from "./topology.js";
|
|
36
39
|
/** Template-relative prefix every `apps/web` route directory sits under. */
|
|
37
40
|
const ROUTE_ROOT = "apps/web/app/[locale]";
|
|
38
41
|
/**
|
|
@@ -98,9 +101,9 @@ function reportFor(route) {
|
|
|
98
101
|
* byte-for-byte in, the target's copy. Non-recursive: nested directories are
|
|
99
102
|
* route dirs of their own, or `__tests__`.
|
|
100
103
|
*/
|
|
101
|
-
function isModified(expectedDir, targetDir) {
|
|
104
|
+
function isModified(expectedDir, targetDir, skip = () => false) {
|
|
102
105
|
for (const entry of readdirSync(expectedDir, { withFileTypes: true })) {
|
|
103
|
-
if (!entry.isFile())
|
|
106
|
+
if (!entry.isFile() || skip(entry.name))
|
|
104
107
|
continue;
|
|
105
108
|
const targetFile = join(targetDir, entry.name);
|
|
106
109
|
if (!existsSync(targetFile))
|
|
@@ -163,12 +166,27 @@ export function syncRoutes(opts) {
|
|
|
163
166
|
skippedSurfaces.push(surface.id);
|
|
164
167
|
}
|
|
165
168
|
skippedSurfaces.sort();
|
|
169
|
+
// #7426 D13 — on a spoke, a route dir the spoke does not carry (the
|
|
170
|
+
// `(auth)` and `(authed)` groups) is out of scope: neither reported nor
|
|
171
|
+
// copied. This is the one filter point for add-module, add-route and the
|
|
172
|
+
// sync-routes subcommand alike; a hub's scope is unchanged. The spoke's
|
|
173
|
+
// sign-in mode rides along (#7894 D1): a hub sign-in spoke keeps its
|
|
174
|
+
// `(authed)` account area in scope and its operator surfaces out.
|
|
175
|
+
const spoke = readSpokeShape(targetAbs);
|
|
176
|
+
// A spoke's own render of a seam, and a file it does not carry, are not
|
|
177
|
+
// "modified" (#7894 D8): the template holds the hub's.
|
|
178
|
+
const spokeSkip = (route) => (name) => {
|
|
179
|
+
const path = `${ROUTE_ROOT}/${route}/${name}`;
|
|
180
|
+
return (spoke !== undefined &&
|
|
181
|
+
(SPOKE_SEAM_FILES.includes(path) || isSpokeExcluded(path, spoke)));
|
|
182
|
+
};
|
|
166
183
|
const inScope = listTemplateRouteDirs(opts.templatesDir)
|
|
167
184
|
.map(reportFor)
|
|
168
185
|
// "authed" (#5014): the `(authed)` route group ships unconditionally in
|
|
169
|
-
// every scaffold
|
|
186
|
+
// every hub scaffold, the same as "core" — it is checked regardless of
|
|
170
187
|
// `carried`, which only tracks GATEABLE_SURFACES opt-in.
|
|
171
|
-
.filter((r) => r.surface === "core" || r.surface === "authed" || carried.has(r.surface))
|
|
188
|
+
.filter((r) => r.surface === "core" || r.surface === "authed" || carried.has(r.surface))
|
|
189
|
+
.filter((r) => spoke === undefined || !isSpokeExcluded(`${ROUTE_ROOT}/${r.route}`, spoke));
|
|
172
190
|
// Identical to scaffoldSync's defaults, so a synced route matches a fresh
|
|
173
191
|
// scaffold of the same target byte-for-byte.
|
|
174
192
|
const markers = {
|
|
@@ -193,7 +211,7 @@ export function syncRoutes(opts) {
|
|
|
193
211
|
if (!existsSync(targetRoute)) {
|
|
194
212
|
missing.push(report);
|
|
195
213
|
}
|
|
196
|
-
else if (isModified(join(scratch, report.route), targetRoute)) {
|
|
214
|
+
else if (isModified(join(scratch, report.route), targetRoute, spokeSkip(report.route))) {
|
|
197
215
|
modified.push(report);
|
|
198
216
|
}
|
|
199
217
|
}
|
|
@@ -241,6 +259,12 @@ export function syncRoutes(opts) {
|
|
|
241
259
|
copyDependencyFile(join(opts.templatesDir, dep), dst, markers);
|
|
242
260
|
appliedDependencies.push(dep);
|
|
243
261
|
}
|
|
262
|
+
// #7874 D9 — a spoke declares what the copied routes read, as the last
|
|
263
|
+
// write. A malformed env_contract block throws EnvContractError naming
|
|
264
|
+
// its line; the files copied above stay.
|
|
265
|
+
if (spoke !== undefined) {
|
|
266
|
+
appendEnvRows(targetAbs, { topology: "spoke", catalog: ENV_CATALOG });
|
|
267
|
+
}
|
|
244
268
|
}
|
|
245
269
|
return {
|
|
246
270
|
targetDir: targetAbs,
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { type ModuleName } from "./parse-args.js";
|
|
2
|
+
/** The two topologies. `hub` is the default and the only one before #7426. */
|
|
3
|
+
export declare const TOPOLOGIES: readonly ["hub", "spoke"];
|
|
4
|
+
export type Topology = (typeof TOPOLOGIES)[number];
|
|
5
|
+
/** A refusal or a malformed input this module names. */
|
|
6
|
+
export declare class TopologyError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/** Roots no spoke carries. `apps/mcp` joins them with `--without-mcp`. */
|
|
10
|
+
export declare const SPOKE_REMOVED_ROOTS: readonly string[];
|
|
11
|
+
/** The root `--without-mcp` removes. */
|
|
12
|
+
export declare const SPOKE_MCP_ROOT = "apps/mcp";
|
|
13
|
+
/** Commerce's apps/web files that reach the hub's own checkout (spoke-intake Rule 3, #7838). */
|
|
14
|
+
export declare const SPOKE_HUB_CHECKOUT_SEEDS: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* The files a spoke does not carry — files and directories, scaffold-relative:
|
|
17
|
+
* the sign-in and database seeds, then the commerce files that call the hub's
|
|
18
|
+
* own checkout (`SPOKE_HUB_CHECKOUT_SEEDS`; a spoke keeps its own, #7838).
|
|
19
|
+
* Every other file that imports one of them goes with it (`spokeExclusions`).
|
|
20
|
+
*/
|
|
21
|
+
export declare const SPOKE_REMOVAL_SEEDS: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Module paths a spoke drops even when it carries the module (#7876 D8,
|
|
24
|
+
* clarified 2026-10-09). `signal-collectors`' report reader links a
|
|
25
|
+
* grant-gated report's bytes on the hub from `NEXT_PUBLIC_AILK_API_URL`,
|
|
26
|
+
* which a spoke refuses (#7874: its hub origin is server-only), and a
|
|
27
|
+
* deliverable is hub-side anyway — so a spoke carries neither the
|
|
28
|
+
* `/reports` routes nor the report seeds that would have no route.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SPOKE_EXCLUDED_MODULE_PATHS: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* The `(authed)` seeds of the sign-in-through-the-hub mode (#7894 D9): the
|
|
33
|
+
* whole `(authed)` group goes in the default mode; in the hub mode its account
|
|
34
|
+
* area, layout and gate seam stay, and only these go — the operator surfaces,
|
|
35
|
+
* the Better Auth gate and the suites that mock `auth.ts` by path.
|
|
36
|
+
*/
|
|
37
|
+
export declare const HUB_MODE_AUTHED_SEEDS: readonly string[];
|
|
38
|
+
/** The generated seam files (D5; #7874 D12). A seam is rewritten, never removed. */
|
|
39
|
+
export declare const WEB_SEAM_PATH = "apps/web/lib/topology.generated.ts";
|
|
40
|
+
export declare const MCP_SEAM_PATH = "apps/mcp/src/lib/topology.generated.ts";
|
|
41
|
+
/** The browser API origin seam (#7874 D12): a spoke's render reads no env. */
|
|
42
|
+
export declare const API_ORIGIN_SEAM_PATH = "apps/web/lib/api-origin.generated.ts";
|
|
43
|
+
/**
|
|
44
|
+
* The `(authed)` layout's signed-out gate (#7894 D8). Under the `(authed)`
|
|
45
|
+
* seed, so a default-mode spoke drops it with its group and never renders it.
|
|
46
|
+
*/
|
|
47
|
+
export declare const AUTHED_GATE_SEAM_PATH = "apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx";
|
|
48
|
+
export declare const SPOKE_SEAM_FILES: readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* Template files only a spoke carries (#7874 D11): the declared-env check.
|
|
51
|
+
* `scaffoldSync` excludes them from a hub's copy (`copyTemplate`'s
|
|
52
|
+
* `excludePaths` matches an exact file path).
|
|
53
|
+
*/
|
|
54
|
+
export declare const SPOKE_ONLY_FILES: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* The closure's allow-list: a file `spokeExclusions` removes must sit under
|
|
57
|
+
* one of these prefixes, or under a `__tests__/` directory. A member outside
|
|
58
|
+
* both is a defect — a shared file started importing a sign-in or database
|
|
59
|
+
* file — and `spokeExclusions` throws naming it rather than cut it silently.
|
|
60
|
+
*/
|
|
61
|
+
export declare const SPOKE_REMOVABLE_PREFIXES: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* A spoke's sign-in modes (#7894 D1): `off` — the default, no sign-in — and
|
|
64
|
+
* `hub`, signing a visitor in through the hub (`--hub-sign-in <origin>`).
|
|
65
|
+
*/
|
|
66
|
+
export type SignInMode = "off" | "hub";
|
|
67
|
+
export interface SpokeShapeOptions {
|
|
68
|
+
/** Whether the spoke keeps `apps/mcp` (the default; `--without-mcp` drops it). */
|
|
69
|
+
mcp: boolean;
|
|
70
|
+
/** The sign-in mode (#7894 D1). Absent means `"off"`, so every existing caller is the default mode. */
|
|
71
|
+
signIn?: SignInMode;
|
|
72
|
+
}
|
|
73
|
+
/** The removed roots for these options. */
|
|
74
|
+
export declare function spokeRemovedRoots(opts: SpokeShapeOptions): string[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether a closure member is one the registry allows a spoke to drop: under
|
|
77
|
+
* a seed of THAT sign-in mode, or a test. In the hub mode a surviving account
|
|
78
|
+
* file that imports `auth.ts` is therefore not removable, and
|
|
79
|
+
* `spokeExclusions` throws naming it (#7894 D9).
|
|
80
|
+
*/
|
|
81
|
+
export declare function isRemovableClosureMember(path: string, opts?: Pick<SpokeShapeOptions, "signIn">): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Every path a spoke does not carry, computed over the built `templates/`
|
|
84
|
+
* tree: the removed roots, the seeds that apply, and every source file under
|
|
85
|
+
* the kept apps that reaches a seed through relative imports, to a fixed
|
|
86
|
+
* point. Sorted; directories and files alike, matched at a directory
|
|
87
|
+
* boundary by `isSpokeExcluded` and `copyTemplate`'s `excludePaths`.
|
|
88
|
+
*
|
|
89
|
+
* The walk is the relative-import resolution `importClosure` uses
|
|
90
|
+
* (`lib/import-graph.ts`), with ONE difference: a seam file is never a
|
|
91
|
+
* candidate, so the walk never crosses it. The hub render of a seam imports
|
|
92
|
+
* a seed by design (D5); its importers keep their place on a spoke because
|
|
93
|
+
* the spoke render of the same file imports none.
|
|
94
|
+
*
|
|
95
|
+
* @throws TopologyError naming every closure member outside
|
|
96
|
+
* `SPOKE_REMOVABLE_PREFIXES` and not a test — a new shared file importing
|
|
97
|
+
* a sign-in or database file, which removing would silently break the
|
|
98
|
+
* spoke (spec § 5.3: escalate, never widen).
|
|
99
|
+
*/
|
|
100
|
+
export declare function spokeExclusions(templatesDir: string, opts: SpokeShapeOptions): string[];
|
|
101
|
+
export interface IsSpokeExcludedOptions extends SpokeShapeOptions {
|
|
102
|
+
/**
|
|
103
|
+
* The full `spokeExclusions(...)` list, when the caller computed it. Absent,
|
|
104
|
+
* only the removed roots and the seeds are checked — exact for any path
|
|
105
|
+
* that is not a file of the built `templates/` tree (a module bundle's
|
|
106
|
+
* file, a route directory), since only a template file can be a closure
|
|
107
|
+
* member.
|
|
108
|
+
*/
|
|
109
|
+
exclusions?: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The one predicate for "a spoke does not carry this path" (the Phase 2
|
|
113
|
+
* generator contract). Scaffold-relative, `/`- or `\`-separated.
|
|
114
|
+
*/
|
|
115
|
+
export declare function isSpokeExcluded(path: string, opts: IsSpokeExcludedOptions): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Where a module bundle's `installMap` destination lands on a spoke (#7876
|
|
118
|
+
* D10): `database/flows/<name>.flow.json` becomes `flows/<name>.json` — a
|
|
119
|
+
* declared flow `create-ailk seed-flows` sends to the hub — and every other
|
|
120
|
+
* destination is returned unchanged. Applied before the spoke filter, so a
|
|
121
|
+
* remapped flow survives it while the rest of `database/` is dropped. A hub
|
|
122
|
+
* install passes no remap.
|
|
123
|
+
*/
|
|
124
|
+
export declare function spokeDestination(destination: string): string;
|
|
125
|
+
/**
|
|
126
|
+
* `apps/web/lib/topology.generated.ts` for `topology` (D5) and, on a spoke,
|
|
127
|
+
* its sign-in mode (#7894 D8). The hub render loads `auth.ts` only when a
|
|
128
|
+
* session is read, so a module that imports this seam for a constant never
|
|
129
|
+
* loads Better Auth.
|
|
130
|
+
*/
|
|
131
|
+
export declare function renderWebSeam(topology: Topology, signIn?: SignInMode): string;
|
|
132
|
+
/**
|
|
133
|
+
* `apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx` (#7894 D8).
|
|
134
|
+
* The hub render is the `(authed)` layout's no-session branch, moved; the
|
|
135
|
+
* `signIn: "hub"` render imports only the hub-session gate. A default-mode
|
|
136
|
+
* spoke carries no `(authed)` group, so it has no render.
|
|
137
|
+
* @throws TopologyError for a spoke in the default mode.
|
|
138
|
+
*/
|
|
139
|
+
export declare function renderAuthedGateSeam(topology: Topology, signIn?: SignInMode): string;
|
|
140
|
+
/** `apps/mcp/src/lib/topology.generated.ts` for `topology` (D5). */
|
|
141
|
+
export declare function renderMcpSeam(topology: Topology): string;
|
|
142
|
+
/** `apps/web/lib/api-origin.generated.ts` for `topology` (#7874 D12). */
|
|
143
|
+
export declare function renderApiOriginSeam(topology: Topology): string;
|
|
144
|
+
/** `pnpm-workspace.yaml` without its `database` package entry. */
|
|
145
|
+
export declare function transformWorkspaceYaml(text: string): string;
|
|
146
|
+
/** The root `package.json` without its `db:*` scripts. */
|
|
147
|
+
export declare function transformRootPackageJson(text: string): string;
|
|
148
|
+
/** The spoke's declared-env script (#7874 D11) — named apart from the monorepo's `env:check`. */
|
|
149
|
+
export declare const ENV_DECLARED_SCRIPT = "env:declared";
|
|
150
|
+
/**
|
|
151
|
+
* The spoke's root `package.json` with the declared-env check (#7874 D11):
|
|
152
|
+
* an `env:declared` script after `claude-md:check`, and `preflight` running
|
|
153
|
+
* it right after `claude-md:check`. Applied after `transformRootPackageJson`.
|
|
154
|
+
* @throws TopologyError when either anchor is missing.
|
|
155
|
+
*/
|
|
156
|
+
export declare function addEnvCheckScripts(text: string): string;
|
|
157
|
+
/**
|
|
158
|
+
* The spoke's `.github/workflows/ci.yml` (#7874 D11): a `pnpm env:declared`
|
|
159
|
+
* step after `pnpm claude-md:check`, and no `pnpm db:generate` step — a
|
|
160
|
+
* spoke has no `database/` and no `db:*` script, so that step would fail
|
|
161
|
+
* first (#7962). The comment block directly above the dropped step goes with
|
|
162
|
+
* it.
|
|
163
|
+
* @throws TopologyError when either anchor is missing.
|
|
164
|
+
*/
|
|
165
|
+
export declare function transformCiWorkflow(text: string): string;
|
|
166
|
+
/** The root `tsconfig.json` without a reference under a removed root. */
|
|
167
|
+
export declare function transformRootTsconfig(text: string, opts: SpokeShapeOptions): string;
|
|
168
|
+
/**
|
|
169
|
+
* An app's `package.json` without its `@working-theory/database` dependency.
|
|
170
|
+
* @throws TopologyError when `required` and the dependency is absent.
|
|
171
|
+
*/
|
|
172
|
+
export declare function transformAppPackageJson(text: string, label: string, required: boolean): string;
|
|
173
|
+
/**
|
|
174
|
+
* Apply every D6 root-file transform to the spoke at `targetDir` (after the
|
|
175
|
+
* base copy, before any module or template layer), and write the spoke
|
|
176
|
+
* render of each seam it carries (D5). Also the declared-env check's root
|
|
177
|
+
* script and CI step (#7874 D11), and the browser API origin seam (D12).
|
|
178
|
+
* @throws TopologyError naming the file whose anchor is missing.
|
|
179
|
+
*/
|
|
180
|
+
export declare function applySpokeTransforms(targetDir: string, opts: SpokeShapeOptions): void;
|
|
181
|
+
/** The env var a spoke reaches its hub through. */
|
|
182
|
+
export declare const HUB_ORIGIN_ENV_VAR = "AILK_API_URL";
|
|
183
|
+
/**
|
|
184
|
+
* Write `AILK_API_URL=<origin>` to the spoke's `apps/web/.env.local` (D2) —
|
|
185
|
+
* the `auth-secret-file.ts` never-clobber rule: an assignment already in any
|
|
186
|
+
* env file Next loads for `apps/web` is kept, and `.env.local` stays
|
|
187
|
+
* gitignored. Never writes `AILK_SPOKE_KEY` or `AILK_VISITOR_FORWARD_KEY`:
|
|
188
|
+
* the hub issues those.
|
|
189
|
+
*/
|
|
190
|
+
export declare function writeHubOriginEnv(targetDir: string, origin: string): void;
|
|
191
|
+
/** The env var naming the hub's web origin a hub-mode spoke signs in at (#7894 D11). */
|
|
192
|
+
export declare const HUB_SIGN_IN_URL_ENV_VAR = "AILK_HUB_SIGN_IN_URL";
|
|
193
|
+
/** The env var holding a hub-mode spoke's own cookie key material (#7894 D7, D11). */
|
|
194
|
+
export declare const HUB_SESSION_SECRET_ENV_VAR = "AILK_HUB_SESSION_SECRET";
|
|
195
|
+
/**
|
|
196
|
+
* Write a hub-mode spoke's sign-in env (#7894 D11) to `apps/web/.env.local`,
|
|
197
|
+
* under `writeHubOriginEnv`'s never-clobber rule: the hub's web origin, and a
|
|
198
|
+
* fresh session secret (32 random bytes, 43 base64url characters). Never the
|
|
199
|
+
* client id or client secret — the hub issues those (`spokes sign-in-client`)
|
|
200
|
+
* — and never `BETTER_AUTH_SECRET` or a database URL.
|
|
201
|
+
*/
|
|
202
|
+
export declare function writeHubSignInEnv(targetDir: string, origin: string): void;
|
|
203
|
+
/** The `hard_floors:` block a spoke's `project.yaml` carries (D9). */
|
|
204
|
+
export declare function renderSpokeHardFloors(opts: SpokeShapeOptions): string;
|
|
205
|
+
/** `project.yaml` with its `hard_floors:` block replaced by the spoke's (D9). */
|
|
206
|
+
export declare function transformProjectYaml(text: string, opts: SpokeShapeOptions): string;
|
|
207
|
+
/** Why a module is not admitted on a spoke, and where its admission lives. */
|
|
208
|
+
export declare const SPOKE_REFUSAL_REASONS: Partial<Record<ModuleName, string>>;
|
|
209
|
+
/** The modules a spoke may carry — every slice declaring `spoke: true`. */
|
|
210
|
+
export declare function spokeModules(): ModuleName[];
|
|
211
|
+
/** The refusal for a module a spoke cannot carry, or undefined when admitted. */
|
|
212
|
+
export declare function spokeModuleRefusal(name: ModuleName): string | undefined;
|
|
213
|
+
/**
|
|
214
|
+
* The topology `<targetDir>` records. A missing manifest, or one without a
|
|
215
|
+
* `topology` key, reads as `"hub"` — every site scaffolded before #7426.
|
|
216
|
+
* @throws TopologyError on an unreadable manifest or an unknown value.
|
|
217
|
+
*/
|
|
218
|
+
export declare function readSiteTopology(targetDir: string): Topology;
|
|
219
|
+
/**
|
|
220
|
+
* The spoke options `<targetDir>` records (#7894 D1), for the later commands
|
|
221
|
+
* (`sync-routes`, `add-module`): `undefined` on a hub; on a spoke, `mcp` from
|
|
222
|
+
* whether it still carries `apps/mcp` and `signIn` from the manifest's
|
|
223
|
+
* `signIn` key — absent reads as `"off"`, every spoke before #7894.
|
|
224
|
+
* @throws TopologyError on an unreadable manifest or an unknown value.
|
|
225
|
+
*/
|
|
226
|
+
export declare function readSpokeShape(targetDir: string): SpokeShapeOptions | undefined;
|
|
227
|
+
/**
|
|
228
|
+
* Hosts a spoke may reach over plain `http:` — the set
|
|
229
|
+
* `packages/api-client/src/http.ts` sends the spoke key under. Named here
|
|
230
|
+
* rather than imported: create-ailk carries no runtime dependency.
|
|
231
|
+
*/
|
|
232
|
+
export declare const LOOPBACK_HOSTS: readonly string[];
|
|
233
|
+
/**
|
|
234
|
+
* The hub origin for a spoke: `--hub-url`, else `AILK_API_URL`. An `https:`
|
|
235
|
+
* URL, or `http:` on a loopback host, with no path but `/`, no query and no
|
|
236
|
+
* fragment — normalized to its origin with no trailing slash.
|
|
237
|
+
* @throws TopologyError naming both inputs when neither is set or the value
|
|
238
|
+
* is not such an origin. Never echoes a query or fragment.
|
|
239
|
+
*/
|
|
240
|
+
export declare function normalizeHubUrl(raw: string | undefined): string;
|
|
241
|
+
/**
|
|
242
|
+
* The hub's WEB origin a hub-mode spoke signs in at (`--hub-sign-in`, #7894
|
|
243
|
+
* D1) — `normalizeHubUrl`'s rules, each refusal naming the flag.
|
|
244
|
+
* @throws TopologyError when `raw` is not such an origin.
|
|
245
|
+
*/
|
|
246
|
+
export declare function normalizeHubSignInUrl(raw: string): string;
|