astroidjs 0.12.1 → 0.13.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.
Files changed (133) hide show
  1. package/README.md +42 -42
  2. package/bin/astroid.mjs +25 -25
  3. package/dist/analytics/index.d.ts +3 -3
  4. package/dist/analytics/index.js +9 -9
  5. package/dist/astro/csp.d.ts +5 -5
  6. package/dist/astro/csp.js +6 -6
  7. package/dist/astro/index.js +1 -1
  8. package/dist/auth/index.d.ts +2 -2
  9. package/dist/auth/index.js +5 -5
  10. package/dist/commerce/adapters.d.ts +6 -6
  11. package/dist/commerce/adapters.js +9 -9
  12. package/dist/commerce/checkout-scaffold.d.ts +5 -5
  13. package/dist/commerce/checkout-scaffold.js +9 -9
  14. package/dist/commerce/checkout.d.ts +12 -12
  15. package/dist/commerce/checkout.js +7 -7
  16. package/dist/commerce/loader.d.ts +2 -2
  17. package/dist/commerce/loader.js +3 -3
  18. package/dist/commerce/mirror.d.ts +4 -4
  19. package/dist/commerce/mirror.js +9 -9
  20. package/dist/commerce/roles.d.ts +10 -10
  21. package/dist/commerce/roles.js +13 -13
  22. package/dist/commerce/secrets.d.ts +9 -9
  23. package/dist/commerce/secrets.js +9 -9
  24. package/dist/commerce/sync.d.ts +7 -7
  25. package/dist/commerce/sync.js +5 -5
  26. package/dist/components/sections.d.ts +9 -9
  27. package/dist/components/sections.js +12 -12
  28. package/dist/config.d.ts +62 -62
  29. package/dist/config.js +18 -18
  30. package/dist/email/inquiry.d.ts +2 -2
  31. package/dist/email/inquiry.js +1 -1
  32. package/dist/email/send.d.ts +4 -4
  33. package/dist/email/send.js +7 -7
  34. package/dist/email/templates.js +3 -3
  35. package/dist/email/theme.d.ts +1 -1
  36. package/dist/email/theme.js +4 -4
  37. package/dist/errors.d.ts +1 -1
  38. package/dist/errors.js +1 -1
  39. package/dist/index.js +1 -1
  40. package/dist/map/pmtiles.d.ts +5 -5
  41. package/dist/map/pmtiles.js +5 -5
  42. package/dist/map/scaffold.d.ts +2 -2
  43. package/dist/map/scaffold.js +4 -4
  44. package/dist/map/style.d.ts +4 -4
  45. package/dist/map/style.js +1 -1
  46. package/dist/portal/config.d.ts +2 -2
  47. package/dist/portal/config.js +3 -3
  48. package/dist/portal/guard.d.ts +4 -4
  49. package/dist/portal/guard.js +4 -4
  50. package/dist/portal/nav.js +2 -2
  51. package/dist/portal/scaffold.d.ts +4 -4
  52. package/dist/portal/scaffold.js +6 -6
  53. package/dist/portal/session.d.ts +2 -2
  54. package/dist/portal/session.js +5 -5
  55. package/dist/portfolio/scaffold.d.ts +1 -1
  56. package/dist/portfolio/scaffold.js +4 -4
  57. package/dist/project/actions.d.ts +1 -1
  58. package/dist/project/actions.js +6 -6
  59. package/dist/project/generate.d.ts +4 -4
  60. package/dist/project/generate.js +15 -15
  61. package/dist/project/index.js +1 -1
  62. package/dist/project/scaffold.d.ts +2 -2
  63. package/dist/project/scaffold.js +11 -11
  64. package/dist/pwa/generate.d.ts +11 -11
  65. package/dist/pwa/generate.js +12 -12
  66. package/dist/queues/consumer.d.ts +3 -3
  67. package/dist/queues/consumer.js +2 -2
  68. package/dist/queues/messages.d.ts +4 -4
  69. package/dist/queues/messages.js +2 -2
  70. package/dist/queues/scaffold.d.ts +4 -4
  71. package/dist/queues/scaffold.js +8 -8
  72. package/dist/queues/webhook.d.ts +5 -5
  73. package/dist/queues/webhook.js +3 -3
  74. package/dist/realtime/scaffold.d.ts +4 -4
  75. package/dist/realtime/scaffold.js +8 -8
  76. package/dist/schema/collections.d.ts +6 -6
  77. package/dist/schema/collections.js +23 -23
  78. package/dist/schema/framework.d.ts +1 -1
  79. package/dist/schema/framework.js +2 -2
  80. package/dist/schema/generate.js +2 -2
  81. package/dist/schema/index.js +1 -1
  82. package/dist/secrets.d.ts +6 -6
  83. package/dist/secrets.js +6 -6
  84. package/dist/security/csp-origins.d.ts +1 -1
  85. package/dist/security/csp-origins.js +3 -3
  86. package/dist/security/rate-rules.d.ts +2 -2
  87. package/dist/security/rate-rules.js +8 -8
  88. package/dist/seo/resolve.d.ts +5 -5
  89. package/dist/seo/resolve.js +2 -2
  90. package/dist/seo/routes.d.ts +5 -5
  91. package/dist/seo/routes.js +3 -3
  92. package/dist/seo/structured-data.d.ts +6 -6
  93. package/dist/seo/structured-data.js +7 -7
  94. package/dist/status.d.ts +5 -5
  95. package/dist/status.js +7 -7
  96. package/dist/tenancy/index.d.ts +3 -3
  97. package/dist/tenancy/index.js +6 -6
  98. package/dist/worker/generate.d.ts +2 -2
  99. package/dist/worker/generate.js +19 -19
  100. package/dist/worker/index.js +1 -1
  101. package/dist/worker/routes.js +1 -1
  102. package/dist/workflow/advance.d.ts +3 -3
  103. package/dist/workflow/advance.js +6 -6
  104. package/dist/workflow/config.d.ts +4 -4
  105. package/dist/workflow/config.js +4 -4
  106. package/dist/workflow/generate.d.ts +2 -2
  107. package/dist/workflow/generate.js +4 -4
  108. package/package.json +3 -4
  109. package/src/components/Collection.tsx +5 -5
  110. package/src/components/Editable.astro +9 -9
  111. package/src/components/JustifiedGallery.astro +8 -8
  112. package/src/components/MediaSlot.astro +12 -12
  113. package/src/components/PortalShell.astro +4 -4
  114. package/src/components/RegisterSW.astro +3 -3
  115. package/src/components/Section.astro +8 -8
  116. package/src/components/Sections.astro +6 -6
  117. package/src/components/Seo.astro +3 -3
  118. package/src/components/StageBar.astro +3 -3
  119. package/src/components/StructuredData.astro +2 -2
  120. package/src/components/justify.ts +9 -9
  121. package/src/components/media-meta.ts +10 -10
  122. package/src/components/sections/AboutIntro.astro +1 -1
  123. package/src/components/sections/Contact.astro +1 -1
  124. package/src/components/sections/Cta.astro +1 -1
  125. package/src/components/sections/Faq.astro +1 -1
  126. package/src/components/sections/FeatureGrid.astro +2 -2
  127. package/src/components/sections/Hero.astro +1 -1
  128. package/src/components/sections/PricingTiers.astro +1 -1
  129. package/src/components/sections/ProductGrid.astro +1 -1
  130. package/src/components/sections/SplitImage.astro +1 -1
  131. package/src/components/sections/Steps.astro +1 -1
  132. package/src/components/sections/Testimonial.astro +1 -1
  133. package/src/components/sections.ts +17 -17
@@ -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 (e.g. `"src/worker.ts"`). */
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 — the files that are a pure function of the Astroid config
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 — the
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 — real ids are filled by
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
  *
@@ -1,18 +1,18 @@
1
1
  // Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
2
2
  //
3
- // Project generation — the config → files layer the `astroid` CLI writes.
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`) — `src/schema.ts`,
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`, …) — `wrangler.jsonc`
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 — so they live in a
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 — the files that are a pure function of the Astroid config
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 — the
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 — no `Date.now()`), bumped deliberately when the runtime
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 — real ids are filled by
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
  *
@@ -120,7 +120,7 @@ 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 — Cloudflare rejects
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");
@@ -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 — a binding with no migration is a deploy error, and the class
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).");
@@ -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 — both come from `astroidCrons`, which is why it exists.
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
@@ -217,8 +217,8 @@ 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
- // so sign-in was impossible on every DEPLOYED site, while every local build
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
224
  p(" // Cloudflare Email Sending — magic-link sign-in + inquiry notifications.");
@@ -242,7 +242,7 @@ export function generateAstroidWrangler(config) {
242
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 — the app id ships to the browser to mount the card
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,7 +251,7 @@ 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 — this is the file someone opens when provisioning, and the list
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) {
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
2
2
  //
3
- // Project generation — config → the files the `astroid` CLI writes (the
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";
@@ -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 — `public/_headers` gets a stanza per module, and a second
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 — the
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[];
@@ -9,8 +9,8 @@
9
9
  // src/worker.ts → import { handleQueueMessage } from "./queue.js"
10
10
  // src/middleware.ts → import { resolvePortalUser } from "./portal-auth.js"
11
11
  //
12
- // The files behind those imports were written in exactly one place —
13
- // `create-astroid`'s CLI — and nothing else could produce them. So turning a
12
+ // The files behind those imports were written in exactly one
13
+ // place—`create-astroid`'s CLI—and nothing else could produce them. So turning a
14
14
  // module on AFTER scaffold, by editing the one typed config the framework is
15
15
  // built around, regenerated a trio importing files that did not exist. `astroid
16
16
  // doctor` reported "healthy" and the project failed in Vite.
@@ -41,7 +41,7 @@ import { generateAstroidQueueSeam, generateAstroidWebhookRoutes } from "../queue
41
41
  * Every scaffold-once file this config implies.
42
42
  *
43
43
  * Ordered by module so a `generate` that writes several prints them in a stable
44
- * sequence. Returns `[]` for a plain marketing site with no modules — the
44
+ * sequence. Returns `[]` for a plain marketing site with no modules—the
45
45
  * baseline floor is entirely the regenerated trio plus the static template.
46
46
  */
47
47
  export function generateAstroidScaffoldFiles(config) {
@@ -56,7 +56,7 @@ export function generateAstroidScaffoldFiles(config) {
56
56
  files.push({ path: "migrations/0003_catalog.sql", contents: catalogSql });
57
57
  // --- the CWV beacon -------------------------------------------------------
58
58
  // A static file under public/, so it is same-origin and covered by
59
- // `script-src 'self'` — an inline script carrying generated content could not
59
+ // `script-src 'self'`—an inline script carrying generated content could not
60
60
  // be hashed into the CSP and would be blocked.
61
61
  const beacon = generateAstroidVitalsBeacon(config, cwvBeaconScript());
62
62
  files.push({ path: beacon.path, contents: beacon.contents });
@@ -96,7 +96,7 @@ export function generateAstroidScaffoldFiles(config) {
96
96
  // is why `astroid generate` must never rewrite them.
97
97
  if (astroidUsesQueues(config)) {
98
98
  files.push({ path: "src/queue.ts", contents: generateAstroidQueueSeam(config) });
99
- // One receiver per provider — a site can run two (invoicing + storefront).
99
+ // One receiver per provider—a site can run two (invoicing + storefront).
100
100
  for (const route of generateAstroidWebhookRoutes(config)) {
101
101
  files.push({ path: route.path, contents: route.contents });
102
102
  }
@@ -107,14 +107,14 @@ export function generateAstroidScaffoldFiles(config) {
107
107
  if (gallery)
108
108
  files.push({ path: "src/pages/work.astro", contents: gallery });
109
109
  // --- pwa: the service worker, manifest, and its headers -------------------
110
- // Static files under public/, not generated source — a service worker is not
110
+ // Static files under public/, not generated source—a service worker is not
111
111
  // bundled, and `_headers` is shared with whatever else writes to it.
112
112
  const sw = generateServiceWorker(config);
113
113
  if (sw) {
114
114
  // `emitDir` puts them where the BROWSER will ask for them. A PWA on its own
115
115
  // subdomain that rewrites to a path prefix (studio.example.com/ → /studio/)
116
- // fetches /sw.js at its own origin root, which rewrites to /studio/sw.js —
117
- // so a worker emitted at the public root is a 404 nothing explains.
116
+ // fetches /sw.js at its own origin root, which rewrites to /studio/sw.js—so
117
+ // a worker emitted at the public root is a 404 nothing explains.
118
118
  const pwaDir = resolvePwa(config).emitDir;
119
119
  const publicBase = pwaDir ? `public/${pwaDir}` : "public";
120
120
  files.push({ path: `${publicBase}/sw.js`, contents: sw });
@@ -125,7 +125,7 @@ export function generateAstroidScaffoldFiles(config) {
125
125
  const headers = generatePwaHeaders(config);
126
126
  if (headers) {
127
127
  files.push({
128
- // `_headers` stays at the public root wherever the worker lives — it is
128
+ // `_headers` stays at the public root wherever the worker lives—it is
129
129
  // one file for the whole site, and Cloudflare only reads it there.
130
130
  path: "public/_headers",
131
131
  contents: headers,
@@ -154,7 +154,7 @@ export function generateAstroidScaffoldFiles(config) {
154
154
  files.push({ path: "src/edit-session.ts", contents: editSession });
155
155
  // --- tenancy: what a subdomain maps to ------------------------------------
156
156
  // Astroid owns the wildcard route and the middleware wiring; this file owns
157
- // every decision — the lookup, its caching, and what an unknown host means.
157
+ // every decision—the lookup, its caching, and what an unknown host means.
158
158
  const tenancy = generateAstroidTenancy(config);
159
159
  if (tenancy)
160
160
  files.push({ path: "src/tenancy.ts", contents: tenancy });
@@ -167,7 +167,7 @@ export function generateAstroidScaffoldFiles(config) {
167
167
  const route = generateAstroidPortalAuthRoute(config);
168
168
  // Always non-null alongside portalAuth (same `astroidPortal` gate), but the
169
169
  // types don't know that and a silent drop here is a portal that cannot
170
- // authenticate — so assert it rather than `?.`-ing it away.
170
+ // authenticate—so assert it rather than `?.`-ing it away.
171
171
  //
172
172
  // The route file lives at the mount path, because Astro routing is
173
173
  // file-path-based: a portal mounted at `/api/shop-auth` needs its catch-all
@@ -21,38 +21,38 @@ export interface PwaConfig {
21
21
  /** Extra paths to precache alongside the scope root. */
22
22
  shell?: string[];
23
23
  /**
24
- * A prerendered page to serve when a navigation fails offline, e.g.
24
+ * A prerendered page to serve when a navigation fails offline, for example,
25
25
  * `"/offline"`.
26
26
  *
27
- * Without it the fallback is the scope root — the *dynamic* app shell, which
27
+ * Without it the fallback is the scope root—the *dynamic* app shell, which
28
28
  * is exactly the wrong thing to precache when the app is auth-gated: that
29
29
  * response carries `Cache-Control: no-store`, so either nothing is cached and
30
30
  * the fallback is empty, or a signed-in shell is stored and later served to
31
31
  * whoever opens the app next.
32
32
  *
33
33
  * Point it at a page with no session-specific markup. It is precached with the
34
- * shell, so it must be prerendered — a dynamic route here fails at exactly the
34
+ * shell, so it must be prerendered—a dynamic route here fails at exactly the
35
35
  * moment it is needed.
36
36
  */
37
37
  offlineFallback?: string;
38
38
  /**
39
- * Subdirectory under `public/` to emit `sw.js` and the manifest into, e.g.
39
+ * Subdirectory under `public/` to emit `sw.js` and the manifest into, for example,
40
40
  * `"studio"`. Default: the public root.
41
41
  *
42
- * For a PWA served from its own subdomain that rewrites to a path prefix —
43
- * `studio.example.com/` → `/studio/` — the browser fetches `/sw.js` at *its*
42
+ * For a PWA served from its own subdomain that rewrites to a path prefix—`studio.example.com/`
43
+ * → `/studio/`—the browser fetches `/sw.js` at *its*
44
44
  * origin root, which rewrites to `/studio/sw.js`. Emitting at the public root
45
45
  * puts the file where nothing will ask for it.
46
46
  *
47
47
  * Set this to the same prefix the host rewrites to. With `scope` equal to the
48
- * serving path, no `Service-Worker-Allowed` header is needed — a worker may
48
+ * serving path, no `Service-Worker-Allowed` header is needed—a worker may
49
49
  * always control its own directory and below.
50
50
  */
51
51
  emitDir?: string;
52
52
  }
53
53
  /** True when this project switched the PWA on. */
54
54
  export declare const usesPwa: (config: AstroidConfig) => boolean;
55
- /** Resolved PWA settings — config over derivation over default. */
55
+ /** Resolved PWA settings—config over derivation over default. */
56
56
  export declare function resolvePwa(config: AstroidConfig): Required<Omit<PwaConfig, "shell" | "offlineFallback" | "emitDir">> & {
57
57
  shell: string[];
58
58
  /** `null` when the app falls back to the scope root (see the config note). */
@@ -63,18 +63,18 @@ export declare function resolvePwa(config: AstroidConfig): Required<Omit<PwaConf
63
63
  /**
64
64
  * `public/manifest.webmanifest`.
65
65
  *
66
- * Icons are declared but NOT generated — a brand's icon is not something a
66
+ * Icons are declared but NOT generated—a brand's icon is not something a
67
67
  * scaffold can invent, and emitting placeholders would produce an installable
68
68
  * app with a grey square for a face. The generated README step says to add them.
69
69
  */
70
70
  export declare function generateWebManifest(config: AstroidConfig): string | null;
71
- /** `public/sw.js`. Plain JS — a service worker is not bundled. */
71
+ /** `public/sw.js`. Plain JS—a service worker is not bundled. */
72
72
  export declare function generateServiceWorker(config: AstroidConfig): string | null;
73
73
  /**
74
74
  * The `public/_headers` block the PWA needs.
75
75
  *
76
76
  * `Service-Worker-Allowed` is emitted ONLY when the scope is broader than the
77
- * script's own location — which, with `sw.js` at the root, never is. Emitting it
77
+ * script's own location—which, with `sw.js` at the root, never is. Emitting it
78
78
  * unconditionally (as the reference does) is harmless but misleading: it implies
79
79
  * a requirement that isn't there, and someone later moving the script will trust
80
80
  * a header that no longer says what they need.
@@ -1,21 +1,21 @@
1
1
  // Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
2
2
  //
3
- // The PWA scaffold — a scoped service worker, a manifest, and the headers they
3
+ // The PWA scaffold—a scoped service worker, a manifest, and the headers they
4
4
  // need.
5
5
  //
6
6
  // The scoping is the whole design, not a detail. A Louise site is CMS-edited:
7
7
  // an editor signs in, flips edit mode on, and edits the live page in place. A
8
8
  // service worker that cached HTML across the whole origin would serve that
9
- // editor a stale copy of the page they are trying to change — and the bug would
9
+ // editor a stale copy of the page they are trying to change—and the bug would
10
10
  // present as "my edits don't save", which is about as far from the cause as a
11
11
  // report can get.
12
12
  //
13
13
  // So the generated worker is scoped, and inside its scope it still refuses to
14
14
  // touch anything dynamic:
15
15
  //
16
- // • `/api/*` never cached — checkout, auth, and every Louise write
17
- // • editor routes never cached — the studio must always be live
18
- // • edit-mode URLs never cached — `?louise` marks a request as an editing
16
+ // • `/api/*` never cached—checkout, auth, and every Louise write
17
+ // • editor routes never cached—the studio must always be live
18
+ // • edit-mode URLs never cached—`?louise` marks a request as an editing
19
19
  // session, and caching one poisons it for everyone
20
20
  //
21
21
  // Everything else is the ordinary split: navigations network-first with a
@@ -23,7 +23,7 @@
23
23
  // when their content does.
24
24
  /** True when this project switched the PWA on. */
25
25
  export const usesPwa = (config) => (config.modules ?? []).includes("pwa");
26
- /** Resolved PWA settings — config over derivation over default. */
26
+ /** Resolved PWA settings—config over derivation over default. */
27
27
  export function resolvePwa(config) {
28
28
  const pwa = config.pwa ?? {};
29
29
  // Normalized to a leading slash and no trailing one (except root), because
@@ -41,7 +41,7 @@ export function resolvePwa(config) {
41
41
  themeColor: pwa.themeColor ?? config.theme.colors.brand,
42
42
  offlineFallback: pwa.offlineFallback ?? null,
43
43
  emitDir: (pwa.emitDir ?? "").replace(/^\/+|\/+$/g, ""),
44
- // The offline page is precached with the shell — a fallback fetched on
44
+ // The offline page is precached with the shell—a fallback fetched on
45
45
  // demand is a fallback that isn't there when the network is.
46
46
  shell: [
47
47
  scope,
@@ -51,7 +51,7 @@ export function resolvePwa(config) {
51
51
  ],
52
52
  };
53
53
  }
54
- /** URL prefix the emitted `sw.js` + manifest are served from — `""` at the
54
+ /** URL prefix the emitted `sw.js` + manifest are served from—`""` at the
55
55
  * public root, `"/studio"` under an `emitDir`. */
56
56
  function assetBase(emitDir) {
57
57
  const dir = (emitDir ?? "").replace(/^\/+|\/+$/g, "");
@@ -60,7 +60,7 @@ function assetBase(emitDir) {
60
60
  /**
61
61
  * `public/manifest.webmanifest`.
62
62
  *
63
- * Icons are declared but NOT generated — a brand's icon is not something a
63
+ * Icons are declared but NOT generated—a brand's icon is not something a
64
64
  * scaffold can invent, and emitting placeholders would produce an installable
65
65
  * app with a grey square for a face. The generated README step says to add them.
66
66
  */
@@ -100,7 +100,7 @@ export function generateWebManifest(config) {
100
100
  ],
101
101
  }, null, 2)}\n`;
102
102
  }
103
- /** `public/sw.js`. Plain JS — a service worker is not bundled. */
103
+ /** `public/sw.js`. Plain JS—a service worker is not bundled. */
104
104
  export function generateServiceWorker(config) {
105
105
  if (!usesPwa(config))
106
106
  return null;
@@ -230,7 +230,7 @@ export function generateServiceWorker(config) {
230
230
  * The `public/_headers` block the PWA needs.
231
231
  *
232
232
  * `Service-Worker-Allowed` is emitted ONLY when the scope is broader than the
233
- * script's own location — which, with `sw.js` at the root, never is. Emitting it
233
+ * script's own location—which, with `sw.js` at the root, never is. Emitting it
234
234
  * unconditionally (as the reference does) is harmless but misleading: it implies
235
235
  * a requirement that isn't there, and someone later moving the script will trust
236
236
  * a header that no longer says what they need.
@@ -238,7 +238,7 @@ export function generateServiceWorker(config) {
238
238
  export function generatePwaHeaders(config) {
239
239
  if (!usesPwa(config))
240
240
  return null;
241
- // Paths must match where the files are actually emitted — a stanza for
241
+ // Paths must match where the files are actually emitted—a stanza for
242
242
  // `/sw.js` while the worker lives at `/studio/sw.js` sets headers on nothing,
243
243
  // and the no-cache rule is what stops a bad worker sticking around.
244
244
  const base = assetBase(config.pwa?.emitDir);
@@ -1,12 +1,12 @@
1
1
  import { type AstroidQueueMessage } from "./messages.js";
2
2
  export interface QueueHandlerOptions {
3
3
  /**
4
- * Re-sync whatever the provider owns — the catalog mirror, a cache. Called
4
+ * Re-sync whatever the provider owns—the catalog mirror, a cache. Called
5
5
  * for a periodic refresh and for webhooks that touched the catalog.
6
6
  *
7
7
  * Receives the message that triggered it, so a site running more than one
8
8
  * commerce provider can branch on `message.provider` rather than refreshing
9
- * everything for everything. A zero-argument seam stays valid — the parameter
9
+ * everything for everything. A zero-argument seam stays valid—the parameter
10
10
  * is there to be ignored until it's needed.
11
11
  *
12
12
  * Throwing marks the message for retry, which is usually right: a failed
@@ -24,7 +24,7 @@ export interface QueueHandlerOptions {
24
24
  * storefront and Square as its POS, at which point `refreshCatalog` means
25
25
  * "re-pull Fourthwall" while Square emits `inventory.count.updated` on every
26
26
  * single sale. Unscoped, a good Saturday becomes a sync storm against an
27
- * unrelated provider's rate limit — and the periodic refresh is unaffected, so
27
+ * unrelated provider's rate limit—and the periodic refresh is unaffected, so
28
28
  * the site looks fine until the day it's busy.
29
29
  */
30
30
  catalogProvider?: string;
@@ -9,8 +9,8 @@
9
9
  // acks as a no-op.
10
10
  //
11
11
  // That last part matters more than it looks. Order, payment, and subscription
12
- // events are read live from the provider, so there is nothing local to update —
13
- // but they still arrive, in volume. A consumer that treats every event as
12
+ // events are read live from the provider, so there is nothing local to
13
+ // update—but they still arrive, in volume. A consumer that treats every event as
14
14
  // actionable turns a busy sales day into a catalog-refresh storm.
15
15
  import { affectsCatalog } from "./messages.js";
16
16
  /**
@@ -14,7 +14,7 @@ export declare const ASTROID_DEFAULT_CRON = "0 * * * *";
14
14
  export declare function astroidCron(config: AstroidConfig): string | null;
15
15
  /**
16
16
  * Daily, at an off-peak-ish minute. The health scan crawls the site's own pages,
17
- * so it is deliberately NOT on the hourly catalog cron — hourly would be a
17
+ * so it is deliberately NOT on the hourly catalog cron—hourly would be a
18
18
  * self-inflicted crawl 24× a day to recompute counts that move slowly.
19
19
  */
20
20
  export declare const ASTROID_HEALTH_CRON = "17 4 * * *";
@@ -31,7 +31,7 @@ export declare const ASTROID_HEALTH_CRON = "17 4 * * *";
31
31
  export declare function astroidCrons(config: AstroidConfig): string[];
32
32
  /** Binding name for the project's queue producer. */
33
33
  export declare const ASTROID_QUEUE_BINDING = "COMMERCE_QUEUE";
34
- /** Queue names derived from the project key — the main queue and its DLQ. */
34
+ /** Queue names derived from the project key—the main queue and its DLQ. */
35
35
  export declare function astroidQueueNames(config: AstroidConfig): {
36
36
  queue: string;
37
37
  dlq: string;
@@ -44,9 +44,9 @@ export declare function astroidQueueNames(config: AstroidConfig): {
44
44
  */
45
45
  export interface WebhookMessage {
46
46
  kind: "webhook";
47
- /** Which integration sent it — `"square"`, `"stripe"`, `"fourthwall"`. */
47
+ /** Which integration sent it—`"square"`, `"stripe"`, `"fourthwall"`. */
48
48
  provider: string;
49
- /** The provider's event type, e.g. `"catalog.version.updated"`. */
49
+ /** The provider's event type, for example, `"catalog.version.updated"`. */
50
50
  type: string;
51
51
  payload: unknown;
52
52
  }
@@ -25,7 +25,7 @@ export function astroidCron(config) {
25
25
  }
26
26
  /**
27
27
  * Daily, at an off-peak-ish minute. The health scan crawls the site's own pages,
28
- * so it is deliberately NOT on the hourly catalog cron — hourly would be a
28
+ * so it is deliberately NOT on the hourly catalog cron—hourly would be a
29
29
  * self-inflicted crawl 24× a day to recompute counts that move slowly.
30
30
  */
31
31
  export const ASTROID_HEALTH_CRON = "17 4 * * *";
@@ -52,7 +52,7 @@ export function astroidCrons(config) {
52
52
  }
53
53
  /** Binding name for the project's queue producer. */
54
54
  export const ASTROID_QUEUE_BINDING = "COMMERCE_QUEUE";
55
- /** Queue names derived from the project key — the main queue and its DLQ. */
55
+ /** Queue names derived from the project key—the main queue and its DLQ. */
56
56
  export function astroidQueueNames(config) {
57
57
  return { queue: `${config.key}-commerce`, dlq: `${config.key}-commerce-dlq` };
58
58
  }
@@ -11,7 +11,7 @@ import type { AstroidConfig, CommerceProvider } from "../config.js";
11
11
  */
12
12
  export declare function generateAstroidEnvBindings(config: AstroidConfig): string;
13
13
  /**
14
- * `src/queue.ts` — the consumer seam the generated worker imports.
14
+ * `src/queue.ts`—the consumer seam the generated worker imports.
15
15
  *
16
16
  * `astroidQueueHandler` already owns the dispatch every site wrote (periodic
17
17
  * refresh, catalog-affecting webhook, no-op for everything else); what's left
@@ -20,14 +20,14 @@ export declare function generateAstroidEnvBindings(config: AstroidConfig): strin
20
20
  */
21
21
  export declare function generateAstroidQueueSeam(config: AstroidConfig): string;
22
22
  /**
23
- * The provider webhook receiver — `src/pages/api/webhooks/<provider>.ts`.
23
+ * The provider webhook receiver—`src/pages/api/webhooks/<provider>.ts`.
24
24
  *
25
25
  * Thin on purpose: `handleWebhook` owns the ordering (verify the raw body before
26
26
  * parsing) and the status-code contract (which codes ask the provider to retry
27
27
  * and which tell it to stop). What's here is the provider's own header and
28
28
  * verifier, plus the secret read.
29
29
  *
30
- * Returns null when the project has no commerce provider — nothing to receive.
30
+ * Returns null when the project has no commerce provider—nothing to receive.
31
31
  */
32
32
  export declare function generateAstroidWebhookRoute(config: AstroidConfig, forProvider?: CommerceProvider): string | null;
33
33
  /**
@@ -35,7 +35,7 @@ export declare function generateAstroidWebhookRoute(config: AstroidConfig, forPr
35
35
  *
36
36
  * Plural because roles are: a site running Stripe for invoicing beside
37
37
  * Fourthwall for the storefront receives from both, each with its own signing
38
- * secret and header. One route per provider, not per role — a provider filling
38
+ * secret and header. One route per provider, not per role—a provider filling
39
39
  * two roles still has one endpoint and one secret.
40
40
  */
41
41
  export declare function generateAstroidWebhookRoutes(config: AstroidConfig): {
@@ -3,7 +3,7 @@
3
3
  // The two SCAFFOLD-ONCE files the queue pipeline needs: the consumer seam
4
4
  // (`src/queue.ts`) and the provider webhook receiver.
5
5
  //
6
- // Deliberately not part of the regenerated trio. Both exist to be edited — the
6
+ // Deliberately not part of the regenerated trio. Both exist to be edited—the
7
7
  // consumer is where a project says what a catalog refresh actually does, and the
8
8
  // webhook route is where it narrows which events it cares about. Regenerating
9
9
  // over them would erase exactly the work they're for. The same boundary
@@ -15,7 +15,7 @@ import { ASTROID_QUEUE_BINDING } from "./messages.js";
15
15
  /**
16
16
  * Per-provider webhook facts: the header, the verifier, and how it's called.
17
17
  *
18
- * The signing-secret NAME is deliberately not here — it's one field of a
18
+ * The signing-secret NAME is deliberately not here—it's one field of a
19
19
  * provider's secret set, which `commerce/secrets.ts` owns so the wrangler
20
20
  * generator, the status report, and this scaffold all read the same list.
21
21
  */
@@ -66,7 +66,7 @@ export function generateAstroidEnvBindings(config) {
66
66
  // One secret SET per provider: a site running Stripe for invoicing and
67
67
  // Fourthwall for the storefront talks to both, credentialed and signed
68
68
  // independently. Every one is optional, because every one is allowed to be
69
- // absent — that's what leaves the module dormant rather than broken.
69
+ // absent—that's what leaves the module dormant rather than broken.
70
70
  ...providers.flatMap((provider) => [
71
71
  ` /** ${provider} API credentials. Absent or still holding the`,
72
72
  " * DUMMY_REPLACE_ME sentinel reads as unconfigured, which leaves commerce",
@@ -80,7 +80,7 @@ export function generateAstroidEnvBindings(config) {
80
80
  ].join("\n");
81
81
  }
82
82
  /**
83
- * `src/queue.ts` — the consumer seam the generated worker imports.
83
+ * `src/queue.ts`—the consumer seam the generated worker imports.
84
84
  *
85
85
  * `astroidQueueHandler` already owns the dispatch every site wrote (periodic
86
86
  * refresh, catalog-affecting webhook, no-op for everything else); what's left
@@ -88,7 +88,7 @@ export function generateAstroidEnvBindings(config) {
88
88
  * a generated constant.
89
89
  */
90
90
  export function generateAstroidQueueSeam(config) {
91
- // The STOREFRONT provider — it's the one with a catalog to re-sync. An
91
+ // The STOREFRONT provider—it's the one with a catalog to re-sync. An
92
92
  // invoicing-only provider has nothing for this hook to do.
93
93
  const provider = astroidCommerceRoles(config.commerce).storefront;
94
94
  const table = astroidCatalogMirror(config).table;
@@ -169,14 +169,14 @@ export function generateAstroidQueueSeam(config) {
169
169
  .join("\n");
170
170
  }
171
171
  /**
172
- * The provider webhook receiver — `src/pages/api/webhooks/<provider>.ts`.
172
+ * The provider webhook receiver—`src/pages/api/webhooks/<provider>.ts`.
173
173
  *
174
174
  * Thin on purpose: `handleWebhook` owns the ordering (verify the raw body before
175
175
  * parsing) and the status-code contract (which codes ask the provider to retry
176
176
  * and which tell it to stop). What's here is the provider's own header and
177
177
  * verifier, plus the secret read.
178
178
  *
179
- * Returns null when the project has no commerce provider — nothing to receive.
179
+ * Returns null when the project has no commerce provider—nothing to receive.
180
180
  */
181
181
  export function generateAstroidWebhookRoute(config, forProvider) {
182
182
  const provider = forProvider ?? astroidCommerceProviders(config.commerce)[0];
@@ -222,7 +222,7 @@ export function generateAstroidWebhookRoute(config, forProvider) {
222
222
  *
223
223
  * Plural because roles are: a site running Stripe for invoicing beside
224
224
  * Fourthwall for the storefront receives from both, each with its own signing
225
- * secret and header. One route per provider, not per role — a provider filling
225
+ * secret and header. One route per provider, not per role—a provider filling
226
226
  * two roles still has one endpoint and one secret.
227
227
  */
228
228
  export function generateAstroidWebhookRoutes(config) {