astroidjs 0.12.1 → 0.14.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 +42 -42
- package/bin/astroid.mjs +35 -29
- package/dist/analytics/index.d.ts +3 -3
- package/dist/analytics/index.js +11 -11
- package/dist/astro/csp.d.ts +5 -5
- package/dist/astro/csp.js +6 -6
- package/dist/astro/index.js +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +5 -5
- package/dist/commerce/adapters.d.ts +6 -6
- package/dist/commerce/adapters.js +9 -9
- package/dist/commerce/checkout-scaffold.d.ts +5 -5
- package/dist/commerce/checkout-scaffold.js +27 -27
- package/dist/commerce/checkout.d.ts +12 -12
- package/dist/commerce/checkout.js +7 -7
- package/dist/commerce/loader.d.ts +2 -2
- package/dist/commerce/loader.js +3 -3
- package/dist/commerce/mirror.d.ts +4 -4
- package/dist/commerce/mirror.js +12 -12
- package/dist/commerce/roles.d.ts +10 -10
- package/dist/commerce/roles.js +13 -13
- package/dist/commerce/secrets.d.ts +9 -9
- package/dist/commerce/secrets.js +9 -9
- package/dist/commerce/sync.d.ts +7 -7
- package/dist/commerce/sync.js +5 -5
- package/dist/components/sections.d.ts +9 -9
- package/dist/components/sections.js +12 -12
- package/dist/config.d.ts +89 -62
- package/dist/config.js +18 -18
- package/dist/email/inquiry.d.ts +2 -2
- package/dist/email/inquiry.js +1 -1
- package/dist/email/send.d.ts +4 -4
- package/dist/email/send.js +7 -7
- package/dist/email/templates.js +3 -3
- package/dist/email/theme.d.ts +1 -1
- package/dist/email/theme.js +4 -4
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.js +1 -1
- package/dist/map/pmtiles.d.ts +5 -5
- package/dist/map/pmtiles.js +5 -5
- package/dist/map/scaffold.d.ts +2 -2
- package/dist/map/scaffold.js +8 -8
- package/dist/map/style.d.ts +4 -4
- package/dist/map/style.js +1 -1
- package/dist/portal/config.d.ts +3 -3
- package/dist/portal/config.js +11 -10
- package/dist/portal/guard.d.ts +4 -4
- package/dist/portal/guard.js +4 -4
- package/dist/portal/nav.js +2 -2
- package/dist/portal/scaffold.d.ts +4 -4
- package/dist/portal/scaffold.js +13 -13
- package/dist/portal/session.d.ts +11 -5
- package/dist/portal/session.js +10 -5
- package/dist/portfolio/scaffold.d.ts +1 -1
- package/dist/portfolio/scaffold.js +8 -8
- package/dist/project/actions.d.ts +1 -1
- package/dist/project/actions.js +18 -12
- package/dist/project/generate.d.ts +4 -4
- package/dist/project/generate.js +26 -26
- package/dist/project/index.d.ts +1 -0
- package/dist/project/index.js +2 -1
- package/dist/project/scaffold.d.ts +2 -2
- package/dist/project/scaffold.js +69 -13
- package/dist/project/seed.d.ts +22 -0
- package/dist/project/seed.js +214 -0
- package/dist/pwa/generate.d.ts +11 -11
- package/dist/pwa/generate.js +19 -19
- package/dist/queues/consumer.d.ts +3 -3
- package/dist/queues/consumer.js +2 -2
- package/dist/queues/messages.d.ts +4 -4
- package/dist/queues/messages.js +2 -2
- package/dist/queues/scaffold.d.ts +4 -4
- package/dist/queues/scaffold.js +17 -17
- package/dist/queues/webhook.d.ts +5 -5
- package/dist/queues/webhook.js +3 -3
- package/dist/realtime/scaffold.d.ts +4 -4
- package/dist/realtime/scaffold.js +12 -12
- package/dist/schema/collections.d.ts +42 -11
- package/dist/schema/collections.js +43 -42
- package/dist/schema/framework.d.ts +1 -1
- package/dist/schema/framework.js +2 -2
- package/dist/schema/generate.js +6 -6
- package/dist/schema/index.js +1 -1
- package/dist/secrets.d.ts +6 -6
- package/dist/secrets.js +6 -6
- package/dist/security/csp-origins.d.ts +1 -1
- package/dist/security/csp-origins.js +3 -3
- package/dist/security/rate-rules.d.ts +2 -2
- package/dist/security/rate-rules.js +8 -8
- package/dist/seo/resolve.d.ts +5 -5
- package/dist/seo/resolve.js +2 -2
- package/dist/seo/routes.d.ts +5 -5
- package/dist/seo/routes.js +3 -3
- package/dist/seo/structured-data.d.ts +6 -6
- package/dist/seo/structured-data.js +7 -7
- package/dist/status.d.ts +5 -5
- package/dist/status.js +7 -7
- package/dist/tenancy/index.d.ts +3 -3
- package/dist/tenancy/index.js +11 -11
- package/dist/worker/generate.d.ts +2 -2
- package/dist/worker/generate.js +91 -57
- package/dist/worker/index.js +1 -1
- package/dist/worker/routes.js +11 -11
- package/dist/workflow/advance.d.ts +3 -3
- package/dist/workflow/advance.js +6 -6
- package/dist/workflow/config.d.ts +4 -4
- package/dist/workflow/config.js +4 -4
- package/dist/workflow/generate.d.ts +2 -2
- package/dist/workflow/generate.js +11 -11
- package/package.json +3 -4
- package/src/components/Collection.tsx +5 -5
- package/src/components/Editable.astro +9 -9
- package/src/components/JustifiedGallery.astro +8 -8
- package/src/components/MediaSlot.astro +12 -12
- package/src/components/PortalShell.astro +4 -4
- package/src/components/RegisterSW.astro +3 -3
- package/src/components/Section.astro +8 -8
- package/src/components/Sections.astro +6 -6
- package/src/components/Seo.astro +3 -3
- package/src/components/StageBar.astro +3 -3
- package/src/components/StructuredData.astro +2 -2
- package/src/components/justify.ts +9 -9
- package/src/components/media-meta.ts +10 -10
- package/src/components/sections/AboutIntro.astro +1 -1
- package/src/components/sections/Contact.astro +1 -1
- package/src/components/sections/Cta.astro +1 -1
- package/src/components/sections/Faq.astro +1 -1
- package/src/components/sections/FeatureGrid.astro +2 -2
- package/src/components/sections/Hero.astro +1 -1
- package/src/components/sections/PricingTiers.astro +1 -1
- package/src/components/sections/ProductGrid.astro +1 -1
- package/src/components/sections/SplitImage.astro +1 -1
- package/src/components/sections/Steps.astro +1 -1
- package/src/components/sections/Testimonial.astro +1 -1
- package/src/components/sections.ts +17 -17
package/dist/portal/scaffold.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
// The portal's SCAFFOLD-ONCE pieces: the second Better Auth instance, and the
|
|
4
4
|
// `App.Locals` / `CloudflareEnv` additions that come with it.
|
|
5
5
|
//
|
|
6
|
-
// The auth instance is scaffolded rather than generated because a site edits it
|
|
7
|
-
//
|
|
6
|
+
// The auth instance is scaffolded rather than generated because a site edits it—the
|
|
7
|
+
// reset email, the role a new account gets, extra user columns. What
|
|
8
8
|
// Astroid fixes are the three things that must not drift: the mount, the cookie
|
|
9
9
|
// prefix, and the table prefix. Get any of those wrong and the two instances
|
|
10
10
|
// fight over one origin's cookies, which fails intermittently and looks like a
|
|
11
11
|
// session bug rather than a configuration one.
|
|
12
12
|
import { astroidPortal } from "./config.js";
|
|
13
13
|
/**
|
|
14
|
-
* `src/portal-auth.ts
|
|
14
|
+
* `src/portal-auth.ts`—the portal Better Auth instance and its session
|
|
15
15
|
* resolver.
|
|
16
16
|
*
|
|
17
17
|
* Returns null when the project has no portal.
|
|
@@ -21,13 +21,13 @@ export function generateAstroidPortalAuth(config) {
|
|
|
21
21
|
if (!portal)
|
|
22
22
|
return null;
|
|
23
23
|
return [
|
|
24
|
-
"// The PORTAL auth instance
|
|
24
|
+
"// The PORTAL auth instance—customers/members, separate from the editor.",
|
|
25
25
|
"//",
|
|
26
26
|
"// Scaffolded once; yours to edit (the reset email, extra user columns, what",
|
|
27
27
|
"// role a new account gets). Three things should NOT change: the basePath,",
|
|
28
28
|
"// the cookiePrefix, and the tablePrefix. The studio instance keeps Better",
|
|
29
29
|
"// Auth's defaults because the Louise editor client hardcodes them, so this",
|
|
30
|
-
"// one moves
|
|
30
|
+
"// one moves—and if the two ever share a cookie prefix, signing into one",
|
|
31
31
|
"// silently signs you out of the other.",
|
|
32
32
|
'import { astroidMailTheme, magicLinkEmail, passwordResetEmail, resolveMailer, sendTransactional } from "astroidjs";',
|
|
33
33
|
'import { env } from "cloudflare:workers";',
|
|
@@ -41,7 +41,7 @@ export function generateAstroidPortalAuth(config) {
|
|
|
41
41
|
" return getLouiseAuth(env, new URL(request.url).origin, {",
|
|
42
42
|
" rpName: astroidConfig.theme.name,",
|
|
43
43
|
" mailFrom: { email: env.MAIL_FROM, name: astroidConfig.theme.name },",
|
|
44
|
-
" // The portal never sends magic links
|
|
44
|
+
" // The portal never sends magic links—it's email + password—but the",
|
|
45
45
|
" // toolkit's config asks for a renderer, so give it the real one.",
|
|
46
46
|
" renderMagicLinkEmail: ({ url, toEmail }) => magicLinkEmail(MAIL_THEME, { url, toEmail }),",
|
|
47
47
|
` basePath: ${JSON.stringify(portal.basePath)},`,
|
|
@@ -51,14 +51,14 @@ export function generateAstroidPortalAuth(config) {
|
|
|
51
51
|
" minPasswordLength: 8,",
|
|
52
52
|
portal.signUp
|
|
53
53
|
? " // Public sign-up is ON for this project."
|
|
54
|
-
: " // Accounts are provisioned by staff
|
|
54
|
+
: " // Accounts are provisioned by staff—no public sign-up.",
|
|
55
55
|
` disableSignUp: ${!portal.signUp},`,
|
|
56
56
|
" sendResetPassword: async ({ user, url }) => {",
|
|
57
57
|
" // Through `resolveMailer`, NOT a hand-built options object: it is the",
|
|
58
58
|
" // only thing that applies the DUMMY_REPLACE_ME sentinel check. Built by",
|
|
59
59
|
" // hand, a fresh deploy with a real EMAIL binding but a placeholder",
|
|
60
60
|
" // MAIL_FROM read as configured and called the Email API with an envelope",
|
|
61
|
-
' // sender of literally "DUMMY_REPLACE_ME"
|
|
61
|
+
' // sender of literally "DUMMY_REPLACE_ME"—rejected upstream, swallowed',
|
|
62
62
|
" // here, and reported to the user as a reset email that was sent.",
|
|
63
63
|
" const mailer = await resolveMailer(env);",
|
|
64
64
|
" await sendTransactional(mailer, [",
|
|
@@ -66,7 +66,7 @@ export function generateAstroidPortalAuth(config) {
|
|
|
66
66
|
" ]);",
|
|
67
67
|
" },",
|
|
68
68
|
" },",
|
|
69
|
-
" // The portal has its own users
|
|
69
|
+
" // The portal has its own users—never the editor allowlist.",
|
|
70
70
|
" resolveAdmins: () => [],",
|
|
71
71
|
" });",
|
|
72
72
|
"}",
|
|
@@ -97,14 +97,14 @@ export function generateAstroidPortalAuth(config) {
|
|
|
97
97
|
].join("\n");
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
* `src/pages/api/portal-auth/[...all].ts
|
|
100
|
+
* `src/pages/api/portal-auth/[...all].ts`—the portal's Better Auth catch-all,
|
|
101
101
|
* mounted at its own basePath so it never collides with the studio's
|
|
102
102
|
* `/api/auth`.
|
|
103
103
|
*
|
|
104
104
|
* Lives here rather than as a literal in `create-astroid` for the same reason
|
|
105
105
|
* the archetype sections moved (#277): the scaffolder is plain JS, so a drifted
|
|
106
106
|
* import path there is invisible until a user's build fails. It is also the half
|
|
107
|
-
* `generateAstroidPortalAuth` is useless without
|
|
107
|
+
* `generateAstroidPortalAuth` is useless without—`src/portal-auth.ts` exports
|
|
108
108
|
* `handlePortalAuth`, and nothing calls it unless this route exists.
|
|
109
109
|
*
|
|
110
110
|
* Returns null when the project has no portal.
|
|
@@ -126,14 +126,14 @@ export function generateAstroidPortalAuthRoute(config) {
|
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
128
|
* The `App.Locals` member the portal adds, as a block `create-astroid`
|
|
129
|
-
* substitutes into `src/env.d.ts`. Empty without a portal
|
|
129
|
+
* substitutes into `src/env.d.ts`. Empty without a portal—a project that
|
|
130
130
|
* types `portalUser` it never sets is inviting a null-check nobody needs.
|
|
131
131
|
*/
|
|
132
132
|
export function generateAstroidPortalLocals(config) {
|
|
133
133
|
if (!astroidPortal(config))
|
|
134
134
|
return "";
|
|
135
135
|
return [
|
|
136
|
-
" /** The signed-in PORTAL user (customers/members)
|
|
136
|
+
" /** The signed-in PORTAL user (customers/members)—distinct from",
|
|
137
137
|
" * `editor`, which is the studio session. Null when signed out. */",
|
|
138
138
|
' portalUser: import("astroidjs").PortalUser | null;',
|
|
139
139
|
].join("\n");
|
package/dist/portal/session.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { PortalUser } from "./guard.js";
|
|
2
|
-
/** Resolves the portal user for a request, or null when signed out.
|
|
3
|
-
|
|
2
|
+
/** Resolves the portal user for a request, or null when signed out. A site's
|
|
3
|
+
* resolver can return its own richer user type; see {@link resolvePortalSession}. */
|
|
4
|
+
export type PortalSessionResolver<U extends PortalUser = PortalUser> = (request: Request) => Promise<U | null>;
|
|
4
5
|
/**
|
|
5
6
|
* Resolve the portal session at most once per request.
|
|
6
7
|
*
|
|
7
8
|
* Shares the *promise*, not the result, so two callers racing during the same
|
|
8
9
|
* request both await one lookup rather than starting a second.
|
|
10
|
+
*
|
|
11
|
+
* Generic over the site's user type, so the result keeps whatever the site's
|
|
12
|
+
* resolver returns (a customer ID, display initials) instead of narrowing to
|
|
13
|
+
* `PortalUser`. Every caller in a request passes the same resolver, so the
|
|
14
|
+
* shared promise always holds that type.
|
|
9
15
|
*/
|
|
10
|
-
export declare function resolvePortalSession(request: Request, resolve: PortalSessionResolver): Promise<
|
|
11
|
-
/** JSON response helper
|
|
16
|
+
export declare function resolvePortalSession<U extends PortalUser = PortalUser>(request: Request, resolve: PortalSessionResolver<U>): Promise<U | null>;
|
|
17
|
+
/** JSON response helper—the shape every portal API route returns. */
|
|
12
18
|
export declare function json(body: unknown, status?: number, headers?: Record<string, string>): Response;
|
|
13
19
|
/** True when the request came from this same origin. */
|
|
14
20
|
export declare function isSameOrigin(request: Request): boolean;
|
|
@@ -20,7 +26,7 @@ export type CustomerGuardResult = {
|
|
|
20
26
|
response: Response;
|
|
21
27
|
};
|
|
22
28
|
/**
|
|
23
|
-
* Guard a portal API handler: a signed-in user, and
|
|
29
|
+
* Guard a portal API handler: a signed-in user, and—on mutations—a
|
|
24
30
|
* same-origin request.
|
|
25
31
|
*
|
|
26
32
|
* ```ts
|
package/dist/portal/session.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
// The middleware resolves it (to gate routes) and so does whatever handler runs
|
|
6
6
|
// next (to know who's asking). Both hitting the session store is a wasted D1
|
|
7
7
|
// round-trip on every authenticated request, so the in-flight promise is shared
|
|
8
|
-
// per request via a `WeakMap
|
|
8
|
+
// per request via a `WeakMap`—keyed on the `Request`, which means entries
|
|
9
9
|
// disappear with the request rather than needing eviction.
|
|
10
10
|
//
|
|
11
11
|
// `requireCustomer` then adds the check a session alone doesn't give you:
|
|
12
12
|
// same-origin on mutations. A cookie is attached by the browser to any request
|
|
13
|
-
// to this origin, including one a third-party page triggered
|
|
13
|
+
// to this origin, including one a third-party page triggered—so a session
|
|
14
14
|
// proves identity, and the origin check proves intent.
|
|
15
15
|
const inFlight = new WeakMap();
|
|
16
16
|
/**
|
|
@@ -18,6 +18,11 @@ const inFlight = new WeakMap();
|
|
|
18
18
|
*
|
|
19
19
|
* Shares the *promise*, not the result, so two callers racing during the same
|
|
20
20
|
* request both await one lookup rather than starting a second.
|
|
21
|
+
*
|
|
22
|
+
* Generic over the site's user type, so the result keeps whatever the site's
|
|
23
|
+
* resolver returns (a customer ID, display initials) instead of narrowing to
|
|
24
|
+
* `PortalUser`. Every caller in a request passes the same resolver, so the
|
|
25
|
+
* shared promise always holds that type.
|
|
21
26
|
*/
|
|
22
27
|
export function resolvePortalSession(request, resolve) {
|
|
23
28
|
const existing = inFlight.get(request);
|
|
@@ -29,7 +34,7 @@ export function resolvePortalSession(request, resolve) {
|
|
|
29
34
|
inFlight.set(request, promise);
|
|
30
35
|
return promise;
|
|
31
36
|
}
|
|
32
|
-
/** JSON response helper
|
|
37
|
+
/** JSON response helper—the shape every portal API route returns. */
|
|
33
38
|
export function json(body, status = 200, headers = {}) {
|
|
34
39
|
return new Response(JSON.stringify(body), {
|
|
35
40
|
status,
|
|
@@ -46,7 +51,7 @@ export function isSameOrigin(request) {
|
|
|
46
51
|
return origin === target;
|
|
47
52
|
// No Origin header: browsers always send one on cross-origin mutations, so
|
|
48
53
|
// its absence means a same-origin or non-browser caller. Fall back to Referer
|
|
49
|
-
// when present, and allow otherwise
|
|
54
|
+
// when present, and allow otherwise—being stricter would break legitimate
|
|
50
55
|
// server-to-server callers without stopping a real CSRF, which always carries
|
|
51
56
|
// an Origin.
|
|
52
57
|
const referer = request.headers.get("referer");
|
|
@@ -61,7 +66,7 @@ export function isSameOrigin(request) {
|
|
|
61
66
|
return true;
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
|
-
* Guard a portal API handler: a signed-in user, and
|
|
69
|
+
* Guard a portal API handler: a signed-in user, and—on mutations—a
|
|
65
70
|
* same-origin request.
|
|
66
71
|
*
|
|
67
72
|
* ```ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AstroidConfig } from "../config.js";
|
|
2
2
|
/**
|
|
3
|
-
* `src/pages/work.astro
|
|
3
|
+
* `src/pages/work.astro`—the portfolio gallery. Null for any other archetype.
|
|
4
4
|
*
|
|
5
5
|
* Intrinsic `width`/`height` are carried through deliberately: they feed the
|
|
6
6
|
* pre-decode layout, so a library with dimensions recorded lays out correctly on
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
// The `portfolio` archetype's scaffold-once page: a justified gallery over the
|
|
4
4
|
// media library.
|
|
5
5
|
//
|
|
6
|
-
// Scaffold-once, not regenerated, for the usual reason
|
|
6
|
+
// Scaffold-once, not regenerated, for the usual reason—this is the first file
|
|
7
7
|
// a portfolio site edits (which assets appear, in what order, whether tiles link
|
|
8
8
|
// to a detail page), so `astroid generate` must never rewrite it.
|
|
9
9
|
//
|
|
10
10
|
// It exists because the primitives alone don't finish the job. `<MediaSlot>` and
|
|
11
11
|
// `<JustifiedGallery>` are archetype-agnostic, but the wiring between them and
|
|
12
|
-
// the media registry
|
|
12
|
+
// the media registry—the public URL shape, filtering to images, carrying
|
|
13
13
|
// alt/caption and intrinsic dimensions through so the first paint isn't a guess
|
|
14
|
-
|
|
14
|
+
//—is identical every time, and is exactly what the consuming sites hand-wrote.
|
|
15
15
|
/**
|
|
16
|
-
* `src/pages/work.astro
|
|
16
|
+
* `src/pages/work.astro`—the portfolio gallery. Null for any other archetype.
|
|
17
17
|
*
|
|
18
18
|
* Intrinsic `width`/`height` are carried through deliberately: they feed the
|
|
19
19
|
* pre-decode layout, so a library with dimensions recorded lays out correctly on
|
|
@@ -25,7 +25,7 @@ export function generateAstroidGalleryPage(config) {
|
|
|
25
25
|
const mediaBase = config.deploy?.mediaBase ?? "/media";
|
|
26
26
|
return [
|
|
27
27
|
"---",
|
|
28
|
-
"// The work gallery
|
|
28
|
+
"// The work gallery—a justified grid over the media library.",
|
|
29
29
|
"//",
|
|
30
30
|
"// Scaffolded once; yours to edit. The layout primitive is general",
|
|
31
31
|
"// (astroidjs/components/JustifiedGallery.astro); what lives here is this",
|
|
@@ -33,8 +33,8 @@ export function generateAstroidGalleryPage(config) {
|
|
|
33
33
|
"//",
|
|
34
34
|
"// Rows carry `alt`/`caption` from the media registry, so an editor fixes alt",
|
|
35
35
|
"// text once in the library and every gallery showing that asset picks it up.",
|
|
36
|
-
"// Assets missing width/height still render
|
|
37
|
-
"// once the image decodes
|
|
36
|
+
"// Assets missing width/height still render—the client corrects the layout",
|
|
37
|
+
"// once the image decodes—but they cost a visible reflow, so it's worth",
|
|
38
38
|
"// backfilling dimensions on older uploads.",
|
|
39
39
|
'import JustifiedGallery from "astroidjs/components/JustifiedGallery.astro";',
|
|
40
40
|
'import type { GalleryItem } from "astroidjs/components/justify";',
|
|
@@ -59,7 +59,7 @@ export function generateAstroidGalleryPage(config) {
|
|
|
59
59
|
" ).all<MediaRow>();",
|
|
60
60
|
" rows = result.results ?? [];",
|
|
61
61
|
"} catch {",
|
|
62
|
-
" // No DB binding yet (pre-provision)
|
|
62
|
+
" // No DB binding yet (pre-provision)—render the empty state.",
|
|
63
63
|
"}",
|
|
64
64
|
"",
|
|
65
65
|
"const items: GalleryItem[] = rows.map((row) => ({",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { AstroidConfig } from "../config.js";
|
|
2
|
-
/** `src/actions/index.ts
|
|
2
|
+
/** `src/actions/index.ts`—the typed mutation surface, scaffolded once. */
|
|
3
3
|
export declare function generateAstroidActions(config: AstroidConfig): string;
|
package/dist/project/actions.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
// Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
|
|
2
2
|
//
|
|
3
|
-
// `src/actions/index.ts
|
|
3
|
+
// `src/actions/index.ts`—the Astro-native, typed mutation surface (ADR 0001
|
|
4
4
|
// layer 2), beside the framework-agnostic `/api/louise/*` routes.
|
|
5
5
|
//
|
|
6
6
|
// Astroid generated only the route half. That is not a missing convenience: the
|
|
7
7
|
// two entrypoints write the SAME rows, and the whole reason `@louise-toolkit/astro`
|
|
8
|
-
// exposes these factories is that each one shares the raw route's store
|
|
9
|
-
//
|
|
8
|
+
// exposes these factories is that each one shares the raw route's store
|
|
9
|
+
// path—`applyFieldSave`, `applySettingsPatch`, `applySaveDraft`. A project that wired
|
|
10
10
|
// its own Actions by hand would get a second write path, and a second write path
|
|
11
11
|
// is where validation, sanitization, and draft-merge semantics drift apart
|
|
12
12
|
// silently (#138).
|
|
13
13
|
//
|
|
14
|
-
// So this file is SCAFFOLD-ONCE and is meant to be added to
|
|
15
|
-
// keeps its own bespoke actions right beside these
|
|
14
|
+
// So this file is SCAFFOLD-ONCE and is meant to be added to—the reference site
|
|
15
|
+
// keeps its own bespoke actions right beside these—but the three below come
|
|
16
16
|
// pre-wired against the same tables and the same collection config the generated
|
|
17
17
|
// worker uses.
|
|
18
|
-
/** `src/actions/index.ts
|
|
18
|
+
/** `src/actions/index.ts`—the typed mutation surface, scaffolded once. */
|
|
19
19
|
export function generateAstroidActions(config) {
|
|
20
20
|
const customKeys = config.settings?.customKeys ?? [];
|
|
21
21
|
const extraImageKeys = config.settings?.imageKeys ?? [];
|
|
@@ -29,12 +29,15 @@ export function generateAstroidActions(config) {
|
|
|
29
29
|
? ` columns: ${JSON.stringify(columnsOverride)},`
|
|
30
30
|
: " columns: ASTROID_SETTINGS_COLUMNS,",
|
|
31
31
|
...(customKeys.length ? [` customKeys: ${JSON.stringify(customKeys)},`] : []),
|
|
32
|
+
// The same sanitizers the generated settingsRoute spreads in. An Action
|
|
33
|
+
// writes no GET, so `read` has nothing to do here.
|
|
34
|
+
...(config.settings?.hooks ? [" sanitize: settingsHooks.sanitize,"] : []),
|
|
32
35
|
extraImageKeys.length
|
|
33
36
|
? ` imageKeys: [...ASTROID_SETTINGS_IMAGE_KEYS, ...${JSON.stringify(extraImageKeys)}],`
|
|
34
37
|
: " imageKeys: ASTROID_SETTINGS_IMAGE_KEYS,",
|
|
35
38
|
];
|
|
36
39
|
return [
|
|
37
|
-
"// The typed Astro Actions surface
|
|
40
|
+
"// The typed Astro Actions surface—ADR 0001 layer 2.",
|
|
38
41
|
"//",
|
|
39
42
|
"// Scaffolded once and yours to ADD to: put your own `defineAction`s in the",
|
|
40
43
|
"// `server` object below, alongside these.",
|
|
@@ -54,17 +57,20 @@ export function generateAstroidActions(config) {
|
|
|
54
57
|
" louiseSettingsAction,",
|
|
55
58
|
'} from "@louise-toolkit/astro";',
|
|
56
59
|
"import {",
|
|
57
|
-
|
|
60
|
+
// Only when the Action uses it: a site that overrides the columns gets them
|
|
61
|
+
// as a literal, and an unused import is a lint error in its own file.
|
|
62
|
+
...(columnsOverride ? [] : [" ASTROID_SETTINGS_COLUMNS,"]),
|
|
58
63
|
" ASTROID_SETTINGS_IMAGE_KEYS,",
|
|
59
64
|
" astroidPagesCollection,",
|
|
60
65
|
'} from "astroidjs";',
|
|
61
66
|
'import astroidConfig from "../../astroid.config.js";',
|
|
62
67
|
'import { pages, pagesVersions, siteSettings } from "../schema.js";',
|
|
68
|
+
...(config.settings?.hooks ? ['import { settingsHooks } from "../settings-hooks.js";'] : []),
|
|
63
69
|
"",
|
|
64
70
|
"const pagesCollection = astroidPagesCollection(astroidConfig);",
|
|
65
71
|
"",
|
|
66
72
|
"// Astro v6+ removed `Astro.locals.runtime.env`, so the bindings are resolved",
|
|
67
|
-
"// from `cloudflare:workers
|
|
73
|
+
"// from `cloudflare:workers`—the same env the raw routes read.",
|
|
68
74
|
"const getEnv = () => env as unknown as CloudflareEnv;",
|
|
69
75
|
"",
|
|
70
76
|
"// `getEditor` is left to its default (`locals.editor`), which the generated",
|
|
@@ -74,7 +80,7 @@ export function generateAstroidActions(config) {
|
|
|
74
80
|
"",
|
|
75
81
|
"export const server = {",
|
|
76
82
|
" louise: {",
|
|
77
|
-
" // Inline field save (title, SEO)
|
|
83
|
+
" // Inline field save (title, SEO)—the live, non-versioned path.",
|
|
78
84
|
" save: defineAction(",
|
|
79
85
|
" louiseSaveAction({",
|
|
80
86
|
" ...deps,",
|
|
@@ -87,7 +93,7 @@ export function generateAstroidActions(config) {
|
|
|
87
93
|
" }),",
|
|
88
94
|
" ),",
|
|
89
95
|
"",
|
|
90
|
-
" // The versioned body/sections save
|
|
96
|
+
" // The versioned body/sections save—stages a DRAFT, exactly as",
|
|
91
97
|
" // versionsRoute does, through the same `applySaveDraft`.",
|
|
92
98
|
" saveDraft: defineAction(",
|
|
93
99
|
" louiseSaveDraftAction({",
|
|
@@ -108,7 +114,7 @@ export function generateAstroidActions(config) {
|
|
|
108
114
|
" ...deps,",
|
|
109
115
|
" table: siteSettings,",
|
|
110
116
|
" // The SAME allowlist the generated worker enforces, imported rather",
|
|
111
|
-
" // than copied
|
|
117
|
+
" // than copied—a second literal here is a list that drifts from the",
|
|
112
118
|
" // one the routes check against, and nothing would fail when it did.",
|
|
113
119
|
...settingsExtra,
|
|
114
120
|
' mediaBase: astroidConfig.deploy?.mediaBase ?? "/media",',
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { AstroidConfig } from "../config.js";
|
|
2
2
|
/** A generated file: a project-root-relative POSIX path + its full contents. */
|
|
3
3
|
export interface GeneratedFile {
|
|
4
|
-
/** Path relative to the project root, POSIX-separated (
|
|
4
|
+
/** Path relative to the project root, POSIX-separated (for example, `"src/worker.ts"`). */
|
|
5
5
|
path: string;
|
|
6
6
|
contents: string;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* The regenerated trio
|
|
9
|
+
* The regenerated trio—the files that are a pure function of the Astroid config
|
|
10
10
|
* and carry a "do not hand-edit" banner. `astroid generate` writes exactly these,
|
|
11
11
|
* and `astroid doctor` regenerates them in-memory to diff against disk. Scaffold-
|
|
12
12
|
* once files (wrangler.jsonc, astro.config, auth.ts) are NOT here by design.
|
|
@@ -22,7 +22,7 @@ export declare function generateAstroidProject(config: AstroidConfig): Generated
|
|
|
22
22
|
* names come from {@link commerceSecretNames}, the same declaration the runtime
|
|
23
23
|
* gate and the generated `env.d.ts` read.
|
|
24
24
|
*
|
|
25
|
-
* Empty string when the project enables no module that needs credentials
|
|
25
|
+
* Empty string when the project enables no module that needs credentials—the
|
|
26
26
|
* core secrets (session, Turnstile, mail) are already in the template file, with
|
|
27
27
|
* their own prose.
|
|
28
28
|
*/
|
|
@@ -31,7 +31,7 @@ export declare function generateAstroidSecretsEnv(config: AstroidConfig): string
|
|
|
31
31
|
* Generate a floor `wrangler.jsonc` from the config: the Worker name + editable
|
|
32
32
|
* bindings a baseline Louise site needs (D1, R2 media, the rate-limit + autosave
|
|
33
33
|
* KV, Cloudflare Images), custom-domain routes from `hosts`, and the `vars` the
|
|
34
|
-
* media route + editor read. Binding ids are placeholders
|
|
34
|
+
* media route + editor read. Binding ids are placeholders—real ids are filled by
|
|
35
35
|
* `wrangler … create` (or, later, `astroid deploy`); `astroid doctor` flags any
|
|
36
36
|
* still-unresolved placeholder.
|
|
37
37
|
*
|
package/dist/project/generate.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
// Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
|
|
2
2
|
//
|
|
3
|
-
// Project generation
|
|
3
|
+
// Project generation—the config → files layer the `astroid` CLI writes.
|
|
4
4
|
//
|
|
5
5
|
// Two tiers of generated file, deliberately kept apart:
|
|
6
6
|
//
|
|
7
|
-
// 1. The REGENERATED trio (`generateAstroidProject`)
|
|
7
|
+
// 1. The REGENERATED trio (`generateAstroidProject`)—`src/schema.ts`,
|
|
8
8
|
// `src/worker.ts`, `src/middleware.ts`. Pure functions of the config, marked
|
|
9
9
|
// "do not hand-edit". `astroid generate` (and `dev`/`build`) rewrite these on
|
|
10
10
|
// every run, and `astroid doctor` diffs them to catch drift.
|
|
11
11
|
//
|
|
12
|
-
// 2. The SCAFFOLD-ONCE files (`generateAstroidWrangler`, …)
|
|
12
|
+
// 2. The SCAFFOLD-ONCE files (`generateAstroidWrangler`, …)—`wrangler.jsonc`
|
|
13
13
|
// and friends. `create-astroid` writes them once; the developer then owns
|
|
14
14
|
// them (fills real binding ids, secrets, account). `astroid generate` must
|
|
15
|
-
// NEVER clobber them, or it would wipe provisioned ids
|
|
15
|
+
// NEVER clobber them, or it would wipe provisioned ids—so they live in a
|
|
16
16
|
// separate function the regenerate path doesn't call.
|
|
17
17
|
import { ASTROID_VITALS_BINDING, astroidVitalsDataset } from "../analytics/index.js";
|
|
18
18
|
import { astroidCheckoutVars } from "../commerce/checkout-scaffold.js";
|
|
@@ -25,7 +25,7 @@ import { tenancyZone } from "../tenancy/index.js";
|
|
|
25
25
|
import { generateAstroidSchema } from "../schema/generate.js";
|
|
26
26
|
import { generateAstroidMiddleware, generateAstroidWorker } from "../worker/generate.js";
|
|
27
27
|
/**
|
|
28
|
-
* The regenerated trio
|
|
28
|
+
* The regenerated trio—the files that are a pure function of the Astroid config
|
|
29
29
|
* and carry a "do not hand-edit" banner. `astroid generate` writes exactly these,
|
|
30
30
|
* and `astroid doctor` regenerates them in-memory to diff against disk. Scaffold-
|
|
31
31
|
* once files (wrangler.jsonc, astro.config, auth.ts) are NOT here by design.
|
|
@@ -47,7 +47,7 @@ export function generateAstroidProject(config) {
|
|
|
47
47
|
* names come from {@link commerceSecretNames}, the same declaration the runtime
|
|
48
48
|
* gate and the generated `env.d.ts` read.
|
|
49
49
|
*
|
|
50
|
-
* Empty string when the project enables no module that needs credentials
|
|
50
|
+
* Empty string when the project enables no module that needs credentials—the
|
|
51
51
|
* core secrets (session, Turnstile, mail) are already in the template file, with
|
|
52
52
|
* their own prose.
|
|
53
53
|
*/
|
|
@@ -75,14 +75,14 @@ export function generateAstroidSecretsEnv(config) {
|
|
|
75
75
|
return lines.join("\n");
|
|
76
76
|
}
|
|
77
77
|
// Pinned compatibility date for the emitted Worker. A literal (Astroid's
|
|
78
|
-
// generators are pure
|
|
78
|
+
// generators are pure—no `Date.now()`), bumped deliberately when the runtime
|
|
79
79
|
// baseline moves; matches the reference site's wrangler.jsonc.
|
|
80
80
|
const COMPATIBILITY_DATE = "2026-06-20";
|
|
81
81
|
/**
|
|
82
82
|
* Generate a floor `wrangler.jsonc` from the config: the Worker name + editable
|
|
83
83
|
* bindings a baseline Louise site needs (D1, R2 media, the rate-limit + autosave
|
|
84
84
|
* KV, Cloudflare Images), custom-domain routes from `hosts`, and the `vars` the
|
|
85
|
-
* media route + editor read. Binding ids are placeholders
|
|
85
|
+
* media route + editor read. Binding ids are placeholders—real ids are filled by
|
|
86
86
|
* `wrangler … create` (or, later, `astroid deploy`); `astroid doctor` flags any
|
|
87
87
|
* still-unresolved placeholder.
|
|
88
88
|
*
|
|
@@ -98,7 +98,7 @@ export function generateAstroidWrangler(config) {
|
|
|
98
98
|
const p = (s = "") => lines.push(s);
|
|
99
99
|
p("{");
|
|
100
100
|
p(' "$schema": "node_modules/wrangler/config-schema.json",');
|
|
101
|
-
p(" // The Cloudflare Worker name
|
|
101
|
+
p(" // The Cloudflare Worker name—also the default *.workers.dev subdomain.");
|
|
102
102
|
p(` "name": ${JSON.stringify(key)},`);
|
|
103
103
|
p(" // Pin your account so deploys don't prompt (or set CLOUDFLARE_ACCOUNT_ID).");
|
|
104
104
|
p(' // "account_id": "<your-cloudflare-account-id>",');
|
|
@@ -108,7 +108,7 @@ export function generateAstroidWrangler(config) {
|
|
|
108
108
|
p(" // global_fetch_strictly_public: a fetch to this zone goes through Cloudflare's");
|
|
109
109
|
p(" // front door like any Internet request, past the WAF and bot rules (ADR 0012).");
|
|
110
110
|
p(" // If the daily health scan starts reporting broken links that aren't, a zone");
|
|
111
|
-
p(" // rule is challenging its self-crawl
|
|
111
|
+
p(" // rule is challenging its self-crawl—allow it rather than drop the flag.");
|
|
112
112
|
p(' "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],');
|
|
113
113
|
p(" // @astrojs/cloudflare builds this entry and wires the static assets under dist/.");
|
|
114
114
|
p(' "main": "src/worker.ts",');
|
|
@@ -120,11 +120,11 @@ export function generateAstroidWrangler(config) {
|
|
|
120
120
|
p(` { "pattern": ${JSON.stringify(host)}, "custom_domain": true },`);
|
|
121
121
|
}
|
|
122
122
|
if (tenancy) {
|
|
123
|
-
// A wildcard is a ZONE route, never a custom domain
|
|
123
|
+
// A wildcard is a ZONE route, never a custom domain—Cloudflare rejects
|
|
124
124
|
// `custom_domain: true` on a pattern containing `*`, which is precisely
|
|
125
125
|
// why `hosts` cannot express this.
|
|
126
126
|
p(" // Wildcard tenant hosts (`tenancy.hostPattern`). A zone route, NOT a");
|
|
127
|
-
p(" // custom_domain
|
|
127
|
+
p(" // custom_domain—Cloudflare refuses a wildcard custom domain. The");
|
|
128
128
|
p(" // apex is routed separately above: `*.example.com` does NOT match");
|
|
129
129
|
p(" // `example.com`, so without a `hosts` entry the apex would 404.");
|
|
130
130
|
p(` { "pattern": ${JSON.stringify(`${tenancy.hostPattern}/*`)}, "zone_name": ${JSON.stringify(tenancyZone(tenancy))} },`);
|
|
@@ -137,7 +137,7 @@ export function generateAstroidWrangler(config) {
|
|
|
137
137
|
}
|
|
138
138
|
if (usesRealtime(config)) {
|
|
139
139
|
// The per-page live editing session (ADR 0002). Two halves, and BOTH are
|
|
140
|
-
// required
|
|
140
|
+
// required—a binding with no migration is a deploy error, and the class
|
|
141
141
|
// must also be exported from the worker entry (the generated src/worker.ts
|
|
142
142
|
// re-exports it) or wrangler can't resolve `class_name`.
|
|
143
143
|
p(" // Durable Object: the per-page live editing session (realtime module).");
|
|
@@ -146,7 +146,7 @@ export function generateAstroidWrangler(config) {
|
|
|
146
146
|
p(" },");
|
|
147
147
|
p(" // A DO class needs a migration tag. `new_sqlite_classes` (NOT");
|
|
148
148
|
p(" // `new_classes`) because the session keeps its authoritative state in");
|
|
149
|
-
p(" // `ctx.storage`, which is the SQLite-backed store
|
|
149
|
+
p(" // `ctx.storage`, which is the SQLite-backed store—and the storage");
|
|
150
150
|
p(" // backend cannot be changed after the class is first deployed.");
|
|
151
151
|
p(' "migrations": [');
|
|
152
152
|
p(` { "tag": ${JSON.stringify(ASTROID_REALTIME_MIGRATION_TAG)}, "new_sqlite_classes": [${JSON.stringify(ASTROID_EDIT_SESSION_CLASS)}] }`);
|
|
@@ -154,7 +154,7 @@ export function generateAstroidWrangler(config) {
|
|
|
154
154
|
}
|
|
155
155
|
// Crons. ONE `scheduled` handler receives all of them and tells them apart by
|
|
156
156
|
// `controller.cron`, so this list and the handler's dispatch must agree
|
|
157
|
-
// exactly
|
|
157
|
+
// exactly—both come from `astroidCrons`, which is why it exists.
|
|
158
158
|
//
|
|
159
159
|
// Daily: the site-health scan (broken links, missing alt text, SEO gaps).
|
|
160
160
|
// Hourly (commerce only): the catalog re-sync safety net, so a missed or DLQ'd
|
|
@@ -164,7 +164,7 @@ export function generateAstroidWrangler(config) {
|
|
|
164
164
|
const { queue, dlq } = astroidQueueNames(config);
|
|
165
165
|
p(" // Provider webhooks are verified at the edge, then enqueued here so the");
|
|
166
166
|
p(" // receiver can return fast. Retries + DLQ routing are Cloudflare's, not");
|
|
167
|
-
p(" // the consumer's
|
|
167
|
+
p(" // the consumer's—set them here, not in code.");
|
|
168
168
|
p(` // Create both: \`wrangler queues create ${queue}\` and \`… ${dlq}\`.`);
|
|
169
169
|
p(' "queues": {');
|
|
170
170
|
p(` "producers": [{ "queue": ${JSON.stringify(queue)}, "binding": ${JSON.stringify(ASTROID_QUEUE_BINDING)} }],`);
|
|
@@ -197,17 +197,17 @@ export function generateAstroidWrangler(config) {
|
|
|
197
197
|
p(' "images": { "binding": "IMAGES" },');
|
|
198
198
|
p(" // Analytics Engine: real-visitor Core Web Vitals. Free, and the ingest");
|
|
199
199
|
p(" // route accepts-and-drops without it, so it costs nothing unused. Reading");
|
|
200
|
-
p(" // the p75 back out needs CF_ACCOUNT_ID + CF_API_TOKEN (see .env.example)
|
|
200
|
+
p(" // the p75 back out needs CF_ACCOUNT_ID + CF_API_TOKEN (see .env.example)—");
|
|
201
201
|
p(" // until those are real the Health badge reads 'not measured yet'.");
|
|
202
202
|
p(` "analytics_engine_datasets": [{ "binding": ${JSON.stringify(ASTROID_VITALS_BINDING)}, "dataset": ${JSON.stringify(astroidVitalsDataset(config))} }],`);
|
|
203
203
|
p(" // Workers AI. Powers the editor's rewrite + SEO-suggest buttons and alt-text");
|
|
204
|
-
p(" // generation on upload
|
|
204
|
+
p(" // generation on upload—all of which SHIP IN THE EDITOR DRAWER already and,");
|
|
205
205
|
p(" // without this binding, were permanently invisible: their routes answer 503");
|
|
206
206
|
p(" // and the client hides the button. No account setup beyond the binding, and");
|
|
207
207
|
p(" // every call is editor-gated, so a visitor can never spend your AI budget.");
|
|
208
208
|
p(' "ai": { "binding": "AI" },');
|
|
209
209
|
p(" // KV: RL = the security rate limiter (it also holds the daily site-health");
|
|
210
|
-
p(" // summary under its own key
|
|
210
|
+
p(" // summary under its own key—one small singleton blob, not worth a binding");
|
|
211
211
|
p(" // someone has to remember to provision); DRAFTS = the autosave write-buffer.");
|
|
212
212
|
p(" // Create each: `wrangler kv namespace create <RL|DRAFTS>`.");
|
|
213
213
|
p(' "kv_namespaces": [');
|
|
@@ -217,11 +217,11 @@ export function generateAstroidWrangler(config) {
|
|
|
217
217
|
// Email Sending. NOT optional decoration: `src/env.d.ts` declares EMAIL as a
|
|
218
218
|
// required member, and Better Auth's magic-link path console-logs the link in
|
|
219
219
|
// dev but calls `env.EMAIL.send(...)` unconditionally in production. Without
|
|
220
|
-
// this binding that call is a TypeError on a binding that was never created
|
|
221
|
-
//
|
|
220
|
+
// this binding that call is a TypeError on a binding that was never created—so
|
|
221
|
+
// sign-in was impossible on every DEPLOYED site, while every local build
|
|
222
222
|
// and every CI scaffold passed. Nothing in this repo runs a deployed scaffold,
|
|
223
223
|
// which is why it survived.
|
|
224
|
-
p(" // Cloudflare Email Sending
|
|
224
|
+
p(" // Cloudflare Email Sending—magic-link sign-in + inquiry notifications.");
|
|
225
225
|
p(" // Sign-in DEPENDS on this: in production the magic link is emailed, not logged.");
|
|
226
226
|
p(" // Enable Email Sending for your zone, then verify the address in MAIL_FROM.");
|
|
227
227
|
p(' "send_email": [{ "name": "EMAIL" }],');
|
|
@@ -239,10 +239,10 @@ export function generateAstroidWrangler(config) {
|
|
|
239
239
|
p(" // Turn it on for a PREVIEW deploy first and walk the activation runbook");
|
|
240
240
|
p(" // (docs/adr/0004-edge-caching.md). `caches.default` is not cleared by");
|
|
241
241
|
p(" // Cloudflare Dev Mode or Purge Everything, so a bad prod flip is hard to");
|
|
242
|
-
p(" // undo
|
|
242
|
+
p(" // undo—this feature was reverted twice for exactly that.");
|
|
243
243
|
p(' "ASTROID_EDGE_CACHE": "false",');
|
|
244
244
|
for (const v of astroidCheckoutVars(config)) {
|
|
245
|
-
// Public, not secret
|
|
245
|
+
// Public, not secret—the app id ships to the browser to mount the card
|
|
246
246
|
// field, and the environment is a choice. Keeping them out of the secret
|
|
247
247
|
// roster also keeps them out of the dormancy gate, which asks whether we can
|
|
248
248
|
// safely CALL Square, not whether a card field can render.
|
|
@@ -251,11 +251,11 @@ export function generateAstroidWrangler(config) {
|
|
|
251
251
|
p(" },");
|
|
252
252
|
// Secrets are NOT vars: they belong in .dev.vars locally and in `wrangler
|
|
253
253
|
// secret put` / Secrets Store when deployed. Listing the names here is
|
|
254
|
-
// deliberate
|
|
254
|
+
// deliberate—this is the file someone opens when provisioning, and the list
|
|
255
255
|
// is generated from the same declaration the runtime dormancy gate reads.
|
|
256
256
|
const secretNames = commerceSecretNames(config.commerce);
|
|
257
257
|
if (secretNames.length > 0) {
|
|
258
|
-
p(" // Commerce secrets
|
|
258
|
+
p(" // Commerce secrets—set OUTSIDE this file (it's committed):");
|
|
259
259
|
p(" // local: .dev.vars (see .env.example, seeded with DUMMY_REPLACE_ME)");
|
|
260
260
|
p(" // deployed: `wrangler secret put <NAME>`, or a Secrets Store binding");
|
|
261
261
|
p(" // Until each is real, commerce stays dormant: the D1 mirror serves, the");
|
package/dist/project/index.d.ts
CHANGED
package/dist/project/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
|
|
2
2
|
//
|
|
3
|
-
// Project generation
|
|
3
|
+
// Project generation—config → the files the `astroid` CLI writes (the
|
|
4
4
|
// regenerated schema/worker/middleware trio + the scaffold-once wrangler.jsonc).
|
|
5
5
|
export * from "./generate.js";
|
|
6
6
|
export * from "./actions.js";
|
|
7
7
|
export * from "./scaffold.js";
|
|
8
|
+
export * from "./seed.js";
|
|
@@ -4,7 +4,7 @@ import type { AstroidConfig } from "../config.js";
|
|
|
4
4
|
*
|
|
5
5
|
* `apply` is the whole contract. `"skip"` (the default) leaves an existing file
|
|
6
6
|
* alone. `"append-once"` is for the files a project accumulates into rather than
|
|
7
|
-
* owns outright
|
|
7
|
+
* owns outright—`public/_headers` gets a stanza per module, and a second
|
|
8
8
|
* module must not erase the first one's.
|
|
9
9
|
*/
|
|
10
10
|
export interface ScaffoldFile {
|
|
@@ -23,7 +23,7 @@ export interface ScaffoldFile {
|
|
|
23
23
|
* Every scaffold-once file this config implies.
|
|
24
24
|
*
|
|
25
25
|
* Ordered by module so a `generate` that writes several prints them in a stable
|
|
26
|
-
* sequence. Returns `[]` for a plain marketing site with no modules
|
|
26
|
+
* sequence. Returns `[]` for a plain marketing site with no modules—the
|
|
27
27
|
* baseline floor is entirely the regenerated trio plus the static template.
|
|
28
28
|
*/
|
|
29
29
|
export declare function generateAstroidScaffoldFiles(config: AstroidConfig): ScaffoldFile[];
|