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,6 +1,6 @@
1
1
  import type { AstroidQueueMessage } from "./messages.js";
2
2
  /**
3
- * The queue producer surface used here — structural, so a real `Queue<T>`
3
+ * The queue producer surface used here—structural, so a real `Queue<T>`
4
4
  * binding satisfies it without astroid depending on the Workers types.
5
5
  *
6
6
  * `Promise<unknown>` rather than `Promise<void>`: Cloudflare's `Queue.send`
@@ -15,18 +15,18 @@ export interface WebhookVerifyInput {
15
15
  raw: string;
16
16
  headers: Headers;
17
17
  url: URL;
18
- /** The signing secret — already checked to be real by the caller. */
18
+ /** The signing secret—already checked to be real by the caller. */
19
19
  secret: string;
20
20
  }
21
21
  export interface WebhookRouteOptions {
22
- /** Which integration this endpoint serves — carried into the message. */
22
+ /** Which integration this endpoint serves—carried into the message. */
23
23
  provider: string;
24
24
  /**
25
25
  * The signing secret, or `null` when unprovisioned. Read it with
26
26
  * `readModuleSecret` so a placeholder counts as absent.
27
27
  */
28
28
  secret: string | null;
29
- /** Signature check over the raw body — e.g. `verifySquareSignature`. */
29
+ /** Signature check over the raw body—for example, `verifySquareSignature`. */
30
30
  verify: (input: WebhookVerifyInput) => boolean | Promise<boolean>;
31
31
  /** The queue binding, or null/undefined when Queues aren't provisioned. */
32
32
  queue?: QueueProducer | null;
@@ -38,7 +38,7 @@ export interface WebhookRouteOptions {
38
38
  eventType?: (payload: unknown) => string;
39
39
  /**
40
40
  * Decide whether an event is worth queueing at all. Returning false acks the
41
- * delivery without enqueuing — the provider is satisfied and the consumer
41
+ * delivery without enqueuing—the provider is satisfied and the consumer
42
42
  * isn't woken for an event nothing acts on.
43
43
  */
44
44
  accept?: (type: string, payload: unknown) => boolean;
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // All three sites wrote this route the same way, and the ordering is the part
6
6
  // worth encoding. **Verify the HMAC over the raw body before parsing anything.**
7
- // Not for style — parsing first means an unauthenticated caller can reach the
7
+ // Not for style—parsing first means an unauthenticated caller can reach the
8
8
  // JSON parser and everything downstream of it, and re-serializing a parsed body
9
9
  // to check the signature is how signature checks quietly stop checking anything.
10
10
  // So the raw text is read once, verified, and only then parsed.
@@ -58,7 +58,7 @@ export async function handleWebhook(request, url, options) {
58
58
  payload = JSON.parse(raw);
59
59
  }
60
60
  catch {
61
- // Also terminal — a body that isn't JSON now won't become JSON later.
61
+ // Also terminal—a body that isn't JSON now won't become JSON later.
62
62
  return text("Invalid JSON", 400);
63
63
  }
64
64
  const type = (options.eventType ?? defaultEventType)(payload);
@@ -76,6 +76,6 @@ export async function handleWebhook(request, url, options) {
76
76
  return text("Queue unavailable", 503);
77
77
  }
78
78
  // 202, not 200: the work hasn't happened yet, it's been accepted. That's the
79
- // entire point of enqueuing — the response returns before the consumer runs.
79
+ // entire point of enqueuing—the response returns before the consumer runs.
80
80
  return text("Accepted", 202);
81
81
  }
@@ -10,12 +10,12 @@ export declare const ASTROID_REALTIME_MIGRATION_TAG = "v1";
10
10
  /** Is the realtime module switched on for this project? */
11
11
  export declare function usesRealtime(config: AstroidConfig): boolean;
12
12
  /**
13
- * `src/edit-session.ts` — the site-owned Durable Object subclass.
13
+ * `src/edit-session.ts`—the site-owned Durable Object subclass.
14
14
  *
15
15
  * Scaffold-once: `persist` is where a project decides what a flush means, and
16
16
  * the lock/field sets are tuning. What Astroid fixes is the delegation shape,
17
17
  * because getting it wrong fails in ways that look like anything but a bug in
18
- * this file — a missing `webSocketClose` leaks presence forever, a non-lazy
18
+ * this file—a missing `webSocketClose` leaks presence forever, a non-lazy
19
19
  * session breaks after the first hibernation wake.
20
20
  *
21
21
  * Returns null when the project has no realtime module.
@@ -23,8 +23,8 @@ export declare function usesRealtime(config: AstroidConfig): boolean;
23
23
  export declare function generateAstroidEditSession(config: AstroidConfig): string | null;
24
24
  /**
25
25
  * The `CloudflareEnv` member the realtime module adds, as a block
26
- * `create-astroid` substitutes into `src/env.d.ts`. Empty without the module —
27
- * a project that types a binding its wrangler.jsonc never creates is making a
26
+ * `create-astroid` substitutes into `src/env.d.ts`. Empty without the module—a
27
+ * project that types a binding its wrangler.jsonc never creates is making a
28
28
  * promise it doesn't keep.
29
29
  */
30
30
  export declare function generateAstroidRealtimeEnv(config: AstroidConfig): string;
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // The package description has claimed "multi-editor sites" since 0.1.0, and this
7
7
  // is the half that makes it true for two people on the SAME page. (The other
8
- // axis — multi-EDITOR, i.e. an org of accounts — was always real.) Without it
8
+ // axis—multi-EDITOR, that is, an org of accounts—was always real.) Without it
9
9
  // two editors on one page clobber each other; the server-side draft merge
10
10
  // narrows the window but there is no live channel, no presence, and no signal
11
11
  // that someone else is in the same field.
@@ -13,15 +13,15 @@
13
13
  // What Astroid generates and what it deliberately does NOT:
14
14
  //
15
15
  // - The DO SUBCLASS is scaffold-once (`src/edit-session.ts`), because it must
16
- // import `cloudflare:workers` — a runtime-only specifier the toolkit can't
17
- // carry — and because its `persist` is the seam a project tunes. Louise
16
+ // import `cloudflare:workers`—a runtime-only specifier the toolkit can't
17
+ // carry—and because its `persist` is the seam a project tunes. Louise
18
18
  // ships the session LOGIC it delegates to; this is the boilerplate around it.
19
19
  // - The wrangler `durable_objects` binding + `migrations` block, which is the
20
20
  // part nobody gets right from memory: a DO class needs a migration tag, and
21
21
  // a SQLite-backed one needs `new_sqlite_classes` rather than `new_classes`.
22
22
  // - The `realtimeRoute` upgrade endpoint, in the generated worker.
23
23
  //
24
- // Persistence goes through `applySaveDraft` — the SAME path the fetch auto-save
24
+ // Persistence goes through `applySaveDraft`—the SAME path the fetch auto-save
25
25
  // uses. One write path, per the ADR: the DO is a new front end to it, not a
26
26
  // parallel store, so drafts, version history, publish, and read-your-writes all
27
27
  // stay intact.
@@ -38,12 +38,12 @@ export function usesRealtime(config) {
38
38
  return (config.modules ?? []).includes("realtime");
39
39
  }
40
40
  /**
41
- * `src/edit-session.ts` — the site-owned Durable Object subclass.
41
+ * `src/edit-session.ts`—the site-owned Durable Object subclass.
42
42
  *
43
43
  * Scaffold-once: `persist` is where a project decides what a flush means, and
44
44
  * the lock/field sets are tuning. What Astroid fixes is the delegation shape,
45
45
  * because getting it wrong fails in ways that look like anything but a bug in
46
- * this file — a missing `webSocketClose` leaks presence forever, a non-lazy
46
+ * this file—a missing `webSocketClose` leaks presence forever, a non-lazy
47
47
  * session breaks after the first hibernation wake.
48
48
  *
49
49
  * Returns null when the project has no realtime module.
@@ -143,8 +143,8 @@ export function generateAstroidEditSession(config) {
143
143
  }
144
144
  /**
145
145
  * The `CloudflareEnv` member the realtime module adds, as a block
146
- * `create-astroid` substitutes into `src/env.d.ts`. Empty without the module —
147
- * a project that types a binding its wrangler.jsonc never creates is making a
146
+ * `create-astroid` substitutes into `src/env.d.ts`. Empty without the module—a
147
+ * project that types a binding its wrangler.jsonc never creates is making a
148
148
  * promise it doesn't keep.
149
149
  */
150
150
  export function generateAstroidRealtimeEnv(config) {
@@ -2,18 +2,18 @@ import { type CollectionConfig, type ContentConfig } from "louise-toolkit/conten
2
2
  import type { AstroidConfig } from "../config.js";
3
3
  /**
4
4
  * Return a copy of a `pages` write payload with its `sections` rich-text fields
5
- * sanitized against the project media base — a no-op when the write carries no
5
+ * sanitized against the project media base—a no-op when the write carries no
6
6
  * `sections`. Pure; leaves every other field (and a partial PATCH's absent ones)
7
7
  * untouched.
8
8
  *
9
9
  * Exported because two write paths need it: the collection's `beforeChange` hook
10
- * below, AND the raw `pagesRoute` (which does not run collection hooks — see
10
+ * below, AND the raw `pagesRoute` (which does not run collection hooks—see
11
11
  * {@link astroidPagesWriteHooks}).
12
12
  */
13
13
  export declare function sanitizeAstroidPageSections(config: AstroidConfig, data: Record<string, unknown>): Record<string, unknown>;
14
14
  /**
15
15
  * Validate the (already-sanitized) `sections` of a `pages` write against the
16
- * catalog, throwing `LouiseValidationError` — an unknown `_type`, a field of the
16
+ * catalog, throwing `LouiseValidationError`—an unknown `_type`, a field of the
17
17
  * wrong shape, or a setting outside its declared options is rejected with a 422
18
18
  * carrying the per-field violations. A no-op when the write carries no
19
19
  * `sections`, so a partial PATCH of other fields isn't spuriously validated.
@@ -23,7 +23,7 @@ export declare function assertAstroidPageSections(config: AstroidConfig, data: R
23
23
  * The write-time hooks the raw `pagesRoute` (louise-toolkit/editor) needs to
24
24
  * enforce the same section contract as the draft path.
25
25
  *
26
- * `pagesRoute` writes straight to the table and — unlike `versionsRoute` — takes
26
+ * `pagesRoute` writes straight to the table and—unlike `versionsRoute`—takes
27
27
  * no collection config, so it never runs the `beforeChange` hook below. Left
28
28
  * bare (as it was), a direct `POST` / `PATCH /api/louise/pages/:id` persists an
29
29
  * unknown section `_type`, a setting outside its options, or unsanitized section
@@ -47,8 +47,8 @@ export declare function astroidPagesWriteHooks(config: AstroidConfig): {
47
47
  export declare function astroidPagesCollection(config: AstroidConfig): CollectionConfig;
48
48
  /**
49
49
  * The Louise `ContentConfig` for an Astroid project. Today: the `pages`
50
- * collection. Archetype- and module-specific collections (e.g. a portfolio
51
- * `gallery`) layer in here as they land — this is the single place that maps
50
+ * collection. Archetype- and module-specific collections (for example, a portfolio
51
+ * `gallery`) layer in here as they land—this is the single place that maps
52
52
  * brand config down to Louise content.
53
53
  */
54
54
  export declare function astroidContentConfig(config: AstroidConfig): ContentConfig;
@@ -3,12 +3,12 @@
3
3
  // Astroid → Louise content mapping. This is the opinionated seam: given an
4
4
  // Astroid project config, derive the Louise `CollectionConfig`(s) a site needs.
5
5
  // Astroid decides WHICH collections and fields exist (its opinions); Louise's
6
- // codegen decides HOW they become D1 tables. Dependency flows one way — this
6
+ // codegen decides HOW they become D1 tables. Dependency flows one way—this
7
7
  // imports `louise-toolkit/content`, never the reverse.
8
8
  // `content/define` and `content/sections` rather than the `content` barrel: the
9
9
  // barrel eagerly pulls the codegen/localApi/validation chunks, and those import
10
- // drizzle-orm for real — an *optional* peer of louise-toolkit, so importing it
11
- // here would force a package on consumers who only DESCRIBE content (e.g.
10
+ // drizzle-orm for real—an *optional* peer of louise-toolkit, so importing it
11
+ // here would force a package on consumers who only DESCRIBE content (for example,
12
12
  // create-astroid's schema generators, which call this function but never run the
13
13
  // beforeChange hook below). Both entries are drizzle-free: `content/define` for
14
14
  // the config types/builders, and `content/sections` for the write-time section
@@ -18,23 +18,23 @@
18
18
  // drizzle-free.
19
19
  //
20
20
  // Note the entry named here is the PUBLIC subpath. Astroid must never reach into
21
- // `louise-toolkit/src/...` — that resolves only because the workspace aliases the
21
+ // `louise-toolkit/src/...`—that resolves only because the workspace aliases the
22
22
  // package to its source, and would break the moment astroid consumes a published
23
23
  // tarball (#327).
24
24
  import { defineCollection, } from "louise-toolkit/content/define";
25
25
  import { assertValidSections, sanitizeSectionsRichText } from "louise-toolkit/content/sections";
26
26
  import { sanitizeRichHtml } from "louise-toolkit/security";
27
- // The catalog is the single declaration of what a section IS — the same object
27
+ // The catalog is the single declaration of what a section IS—the same object
28
28
  // the on-canvas editor mounts with and this hook validates against. It lives
29
29
  // beside the components (it ships as source for them) and is imported here so
30
30
  // the two can't drift.
31
31
  import { astroidSectionCatalog } from "../components/sections.js";
32
32
  /**
33
- * The opinionated `pages` collection — the EDITABLE page fields, versioned
33
+ * The opinionated `pages` collection—the EDITABLE page fields, versioned
34
34
  * drafts, and full-text search. Keyed to the same names as Louise's `pagesColumns`
35
35
  * so a publish's `.set()` maps straight onto the physical columns; bookkeeping
36
36
  * columns (`id`/`status`/timestamps/`publishedVersionId`) live on the table via
37
- * `pagesColumns`, never here — matching the site's `pages-collection.ts`.
37
+ * `pagesColumns`, never here—matching the site's `pages-collection.ts`.
38
38
  *
39
39
  * Validated by `defineCollection` at build time, so a malformed field shape throws
40
40
  * here rather than at codegen.
@@ -58,21 +58,21 @@ function resolveSectionCatalog(config) {
58
58
  * declaring a `blocks` policy, no block is ever reached.
59
59
  *
60
60
  * It is NOT optional once a site does declare one. Unlike the section catalog
61
- * there's no built-in fallback to borrow — block types are wholly site-defined —
62
- * so an unset catalog means every block `_type` reads as unknown and the write
63
- * 422s, and block rich text goes unsanitized because its def can't be resolved.
61
+ * there's no built-in fallback to borrow—block types are wholly site-defined—so
62
+ * an unset catalog means every block `_type` reads as unknown and the write
63
+ * returns 422, and block rich text goes unsanitized because its def can't be resolved.
64
64
  */
65
65
  function resolveBlockCatalog(config) {
66
66
  return config.blockCatalog ?? {};
67
67
  }
68
68
  /**
69
69
  * Return a copy of a `pages` write payload with its `sections` rich-text fields
70
- * sanitized against the project media base — a no-op when the write carries no
70
+ * sanitized against the project media base—a no-op when the write carries no
71
71
  * `sections`. Pure; leaves every other field (and a partial PATCH's absent ones)
72
72
  * untouched.
73
73
  *
74
74
  * Exported because two write paths need it: the collection's `beforeChange` hook
75
- * below, AND the raw `pagesRoute` (which does not run collection hooks — see
75
+ * below, AND the raw `pagesRoute` (which does not run collection hooks—see
76
76
  * {@link astroidPagesWriteHooks}).
77
77
  */
78
78
  export function sanitizeAstroidPageSections(config, data) {
@@ -84,7 +84,7 @@ export function sanitizeAstroidPageSections(config, data) {
84
84
  }
85
85
  /**
86
86
  * Validate the (already-sanitized) `sections` of a `pages` write against the
87
- * catalog, throwing `LouiseValidationError` — an unknown `_type`, a field of the
87
+ * catalog, throwing `LouiseValidationError`—an unknown `_type`, a field of the
88
88
  * wrong shape, or a setting outside its declared options is rejected with a 422
89
89
  * carrying the per-field violations. A no-op when the write carries no
90
90
  * `sections`, so a partial PATCH of other fields isn't spuriously validated.
@@ -102,7 +102,7 @@ export async function assertAstroidPageSections(config, data, operation = "updat
102
102
  * The write-time hooks the raw `pagesRoute` (louise-toolkit/editor) needs to
103
103
  * enforce the same section contract as the draft path.
104
104
  *
105
- * `pagesRoute` writes straight to the table and — unlike `versionsRoute` — takes
105
+ * `pagesRoute` writes straight to the table and—unlike `versionsRoute`—takes
106
106
  * no collection config, so it never runs the `beforeChange` hook below. Left
107
107
  * bare (as it was), a direct `POST` / `PATCH /api/louise/pages/:id` persists an
108
108
  * unknown section `_type`, a setting outside its options, or unsanitized section
@@ -119,12 +119,12 @@ export async function assertAstroidPageSections(config, data, operation = "updat
119
119
  export function astroidPagesWriteHooks(config) {
120
120
  const mediaBase = pageMediaBase(config);
121
121
  return {
122
- // `body` is a richField, so it goes through pagesRoute's own sanitize seam —
123
- // with the project media base, matching the hook rather than the toolkit
122
+ // `body` is a richField, so it goes through pagesRoute's own sanitize seam—with
123
+ // the project media base, matching the hook rather than the toolkit
124
124
  // default sanitizer that knows no media base.
125
125
  sanitize: (html) => sanitizeRichHtml(html, { mediaBase }),
126
126
  // `sections` is not a richField, so it's sanitized here in the transform,
127
- // which pagesRoute runs BEFORE validate — the hook's sanitize-then-validate
127
+ // which pagesRoute runs BEFORE validate—the hook's sanitize-then-validate
128
128
  // order.
129
129
  transform: (data) => sanitizeAstroidPageSections(config, data),
130
130
  validate: (data, ctx) => assertAstroidPageSections(config, data, ctx.operation),
@@ -132,14 +132,14 @@ export function astroidPagesWriteHooks(config) {
132
132
  }
133
133
  export function astroidPagesCollection(config) {
134
134
  // The `body` is rich HTML edited in place (`<Editable type="richtext">`) and
135
- // staged as a draft, so sanitize it on every write — never store raw HTML. A
135
+ // staged as a draft, so sanitize it on every write—never store raw HTML. A
136
136
  // pasted `<img>` pointing off-origin (a hotlink) is dropped: body images must
137
137
  // live in the media library. Mirrors the reference site's pages-collection hook.
138
138
  const mediaBase = pageMediaBase(config);
139
139
  const fields = {};
140
140
  fields.slug = { type: "text", required: true };
141
141
  fields.title = { type: "text", required: true };
142
- // Sanitized rich HTML (a string), not TipTap JSON — matches `pagesColumns.body`.
142
+ // Sanitized rich HTML (a string), not TipTap JSON—matches `pagesColumns.body`.
143
143
  fields.body = { type: "text" };
144
144
  fields.seoTitle = { type: "text" };
145
145
  fields.seoDescription = { type: "text" };
@@ -147,7 +147,7 @@ export function astroidPagesCollection(config) {
147
147
  fields.noindex = { type: "checkbox" };
148
148
  fields.sortOrder = { type: "number" };
149
149
  // Structured page-builder blocks (the editable home), deep-validated against
150
- // the section catalog on write — see the beforeChange hook below.
150
+ // the section catalog on write—see the beforeChange hook below.
151
151
  fields.sections = { type: "json" };
152
152
  return defineCollection({
153
153
  slug: "pages",
@@ -163,7 +163,7 @@ export function astroidPagesCollection(config) {
163
163
  // validating the raw value would pass content the sanitizer is about
164
164
  // to change. Same order as the body above. Both steps are shared with
165
165
  // the raw pagesRoute (see astroidPagesWriteHooks) so the two write
166
- // paths can't diverge — the sanitize throws nothing, the assert throws
166
+ // paths can't diverge—the sanitize throws nothing, the assert throws
167
167
  // LouiseValidationError → 422 with per-field violations.
168
168
  next = sanitizeAstroidPageSections(config, next);
169
169
  await assertAstroidPageSections(config, next, "update");
@@ -177,8 +177,8 @@ export function astroidPagesCollection(config) {
177
177
  }
178
178
  /**
179
179
  * The Louise `ContentConfig` for an Astroid project. Today: the `pages`
180
- * collection. Archetype- and module-specific collections (e.g. a portfolio
181
- * `gallery`) layer in here as they land — this is the single place that maps
180
+ * collection. Archetype- and module-specific collections (for example, a portfolio
181
+ * `gallery`) layer in here as they land—this is the single place that maps
182
182
  * brand config down to Louise content.
183
183
  */
184
184
  export function astroidContentConfig(config) {
@@ -1,7 +1,7 @@
1
1
  import type { AstroidConfig } from "../config.js";
2
2
  /** A ready-made table Astroid re-exports from `louise-toolkit/db`. */
3
3
  export type AstroidFrameworkTable = "inquiries" | "media" | "siteSettings";
4
- /** True when the site captures inquiries — a contact section or a
4
+ /** True when the site captures inquiries—a contact section or a
5
5
  * wholesale-inquiry module. Shared by table selection (here) and route selection
6
6
  * (the worker route plan). */
7
7
  export declare function capturesInquiries(config: AstroidConfig): boolean;
@@ -5,11 +5,11 @@
5
5
  // tables never drift from Louise. `media` and `siteSettings` are universal;
6
6
  // `inquiries` is pulled in only when a brand actually captures inquiries (a
7
7
  // contact section, or a wholesale-inquiry module).
8
- /** True when the site captures inquiries — a contact section or a
8
+ /** True when the site captures inquiries—a contact section or a
9
9
  * wholesale-inquiry module. Shared by table selection (here) and route selection
10
10
  * (the worker route plan). */
11
11
  export function capturesInquiries(config) {
12
- // Explicit override wins — a site whose inquiry surface is a bespoke section
12
+ // Explicit override wins—a site whose inquiry surface is a bespoke section
13
13
  // (coracle's custom `contactForm`) can't be detected from the built-in
14
14
  // vocabulary, so it says so directly.
15
15
  if (typeof config.inquiries === "boolean")
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
2
2
  //
3
- // generateAstroidSchema — emit the Drizzle schema source (a site's `schema.ts`)
3
+ // generateAstroidSchema—emit the Drizzle schema source (a site's `schema.ts`)
4
4
  // from an Astroid config. This is the boilerplate every Louise site hand-writes:
5
5
  // compose the framework `pagesColumns` into a `pages` table, add the versions
6
6
  // table + publish pointer, and re-export the ready-made framework tables. Astroid
@@ -24,7 +24,7 @@ export function generateAstroidSchema(config) {
24
24
  // One import brings them all in (matching the site) so nothing is unused.
25
25
  const dbImports = [...framework, "pagesColumns"].sort();
26
26
  const catalog = generateCatalogTable(config);
27
- // Only import the column builders the emitted source actually uses — an
27
+ // Only import the column builders the emitted source actually uses—an
28
28
  // unused import is a lint error in the project we're generating into, and a
29
29
  // missing one (`real`, from the catalog's price/sortOrder) is a type error.
30
30
  const drizzleImports = ["integer", "sqliteTable", "text"];
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) 2026 BowenLabs. Astroid is MIT licensed.
2
2
  //
3
- // Schema generation — config → Louise content config → Drizzle schema source.
3
+ // Schema generation—config → Louise content config → Drizzle schema source.
4
4
  export * from "./collections.js";
5
5
  export * from "./framework.js";
6
6
  export * from "./generate.js";
package/dist/secrets.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type SecretSource } from "louise-toolkit/security";
2
2
  export type { SecretSource };
3
3
  /**
4
4
  * The placeholder every Astroid scaffold seeds its unprovisioned secrets with.
5
- * Reading it back means "not configured yet", never a credential — the value is
5
+ * Reading it back means "not configured yet", never a credential—the value is
6
6
  * deliberately loud so it is obvious in a Secrets Store listing or a log line.
7
7
  *
8
8
  * It matches the sentinel `louise-toolkit`'s Turnstile gate already recognizes,
@@ -11,7 +11,7 @@ export type { SecretSource };
11
11
  export declare const ASTROID_SECRET_PLACEHOLDER = "DUMMY_REPLACE_ME";
12
12
  /**
13
13
  * Read one secret under the Astroid convention: `null` unless it holds a real,
14
- * non-placeholder value. Thin by design — the reason to call this rather than
14
+ * non-placeholder value. Thin by design—the reason to call this rather than
15
15
  * `readSecret` directly is that it binds Astroid's sentinel for you.
16
16
  */
17
17
  export declare function readModuleSecret(source: SecretSource): Promise<string | null>;
@@ -19,14 +19,14 @@ export declare function readModuleSecret(source: SecretSource): Promise<string |
19
19
  export interface ModuleSecrets<K extends string> {
20
20
  /**
21
21
  * True when EVERY secret the module declared resolved to a real value. The
22
- * module's `isConfigured()` should be exactly this — partial provisioning is
22
+ * module's `isConfigured()` should be exactly this—partial provisioning is
23
23
  * treated as dormant, since a half-configured integration fails at the worst
24
24
  * possible moment (mid-checkout) rather than at boot.
25
25
  */
26
26
  configured: boolean;
27
27
  /** Each declared secret's resolved value, or `null` where unprovisioned. */
28
28
  values: Record<K, string | null>;
29
- /** The still-unprovisioned names, in declaration order — the "why not" list. */
29
+ /** The still-unprovisioned names, in declaration order—the "why not" list. */
30
30
  missing: K[];
31
31
  }
32
32
  /**
@@ -46,9 +46,9 @@ export interface ModuleSecrets<K extends string> {
46
46
  */
47
47
  export declare function resolveModuleSecrets<K extends string>(sources: Record<K, SecretSource>): Promise<ModuleSecrets<K>>;
48
48
  /**
49
- * A one-line, human-readable status for a module — what `astroid doctor`, a dev
49
+ * A one-line, human-readable status for a module—what `astroid doctor`, a dev
50
50
  * server banner, or a health endpoint should print. Naming the missing secrets
51
51
  * is the whole point: "commerce is off" sends someone reading source, "commerce
52
- * is dormant — set SQUARE_ACCESS_TOKEN" does not.
52
+ * is dormant—set SQUARE_ACCESS_TOKEN" does not.
53
53
  */
54
54
  export declare function describeModuleStatus<K extends string>(module: string, status: ModuleSecrets<K>): string;
package/dist/secrets.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // Astroid's optional modules are opt-in at the CONFIG level but not at the
6
6
  // account level: switching `commerce` on in `defineAstroid` must not require a
7
7
  // Square account before `pnpm dev` will boot. So every module here follows one
8
- // rule — a module whose secrets are unprovisioned is DORMANT: it renders, it
8
+ // rule—a module whose secrets are unprovisioned is DORMANT: it renders, it
9
9
  // serves, it says out loud that it is simulated, and it never calls upstream
10
10
  // with a dummy credential.
11
11
  //
@@ -13,7 +13,7 @@
13
13
  // absent / unreadable / empty / a placeholder reads as `null`). What lives here
14
14
  // is the *convention* over it, which is Astroid's opinion, not the toolkit's:
15
15
  //
16
- // 1. ONE sentinel — `ASTROID_SECRET_PLACEHOLDER` — seeded by `create-astroid`
16
+ // 1. ONE sentinel—`ASTROID_SECRET_PLACEHOLDER`—seeded by `create-astroid`
17
17
  // into every secret a scaffold declares, so a fresh clone has a complete,
18
18
  // valid binding set and zero real credentials.
19
19
  // 2. `resolveModuleSecrets` collapses a module's whole secret set into one
@@ -24,7 +24,7 @@
24
24
  import { readSecret } from "louise-toolkit/security";
25
25
  /**
26
26
  * The placeholder every Astroid scaffold seeds its unprovisioned secrets with.
27
- * Reading it back means "not configured yet", never a credential — the value is
27
+ * Reading it back means "not configured yet", never a credential—the value is
28
28
  * deliberately loud so it is obvious in a Secrets Store listing or a log line.
29
29
  *
30
30
  * It matches the sentinel `louise-toolkit`'s Turnstile gate already recognizes,
@@ -33,7 +33,7 @@ import { readSecret } from "louise-toolkit/security";
33
33
  export const ASTROID_SECRET_PLACEHOLDER = "DUMMY_REPLACE_ME";
34
34
  /**
35
35
  * Read one secret under the Astroid convention: `null` unless it holds a real,
36
- * non-placeholder value. Thin by design — the reason to call this rather than
36
+ * non-placeholder value. Thin by design—the reason to call this rather than
37
37
  * `readSecret` directly is that it binds Astroid's sentinel for you.
38
38
  */
39
39
  export function readModuleSecret(source) {
@@ -68,10 +68,10 @@ export async function resolveModuleSecrets(sources) {
68
68
  return { configured: missing.length === 0, values, missing };
69
69
  }
70
70
  /**
71
- * A one-line, human-readable status for a module — what `astroid doctor`, a dev
71
+ * A one-line, human-readable status for a module—what `astroid doctor`, a dev
72
72
  * server banner, or a health endpoint should print. Naming the missing secrets
73
73
  * is the whole point: "commerce is off" sends someone reading source, "commerce
74
- * is dormant — set SQUARE_ACCESS_TOKEN" does not.
74
+ * is dormant—set SQUARE_ACCESS_TOKEN" does not.
75
75
  */
76
76
  export function describeModuleStatus(module, status) {
77
77
  if (status.configured)
@@ -11,6 +11,6 @@ export declare function astroidCspOrigins(config: AstroidConfig): Required<CspOr
11
11
  * `'unsafe-inline'` is required (Louise's data-driven `style=""` carriers and
12
12
  * the editor's runtime `<style>`), which is why Astro can't own this directive:
13
13
  * per spec a single hash here voids `'unsafe-inline'`. Module and config
14
- * `style` origins ride along, e.g. the Square SDK's own stylesheet.
14
+ * `style` origins ride along, for example, the Square SDK's own stylesheet.
15
15
  */
16
16
  export declare function astroidCspStyleSrc(config: AstroidConfig): string;
@@ -31,11 +31,11 @@ const COMMERCE_ORIGINS = {
31
31
  };
32
32
  // Turnstile. Always allowed, not gated on the captcha being configured: the
33
33
  // scaffold ships the widget dormant (see the dormant-until-provisioned
34
- // convention) and it must not need a rebuild to switch on — CSP is baked at
34
+ // convention) and it must not need a rebuild to switch on—CSP is baked at
35
35
  // build time, the secret is a runtime value.
36
36
  const TURNSTILE = turnstileCsp();
37
37
  // The map module. MapLibre spins its tile-decoding workers up from blob: URLs,
38
- // so `worker-src blob:` is not optional — without it the map renders an empty
38
+ // so `worker-src blob:` is not optional—without it the map renders an empty
39
39
  // canvas and the console fills with worker-construction errors.
40
40
  //
41
41
  // Nothing else is needed, and that's the whole argument for the self-hosted
@@ -69,7 +69,7 @@ export function astroidCspOrigins(config) {
69
69
  * `'unsafe-inline'` is required (Louise's data-driven `style=""` carriers and
70
70
  * the editor's runtime `<style>`), which is why Astro can't own this directive:
71
71
  * per spec a single hash here voids `'unsafe-inline'`. Module and config
72
- * `style` origins ride along, e.g. the Square SDK's own stylesheet.
72
+ * `style` origins ride along, for example, the Square SDK's own stylesheet.
73
73
  */
74
74
  export function astroidCspStyleSrc(config) {
75
75
  return ["'self'", "'unsafe-inline'", ...astroidCspOrigins(config).style].join(" ");
@@ -2,7 +2,7 @@ import type { RateRule } from "louise-toolkit/security";
2
2
  import type { AstroidConfig } from "../config.js";
3
3
  export type { RateRule };
4
4
  /**
5
- * Base path the customer/portal Better Auth instance mounts at — its own
5
+ * Base path the customer/portal Better Auth instance mounts at—its own
6
6
  * handler, separate from the editor's `/api/auth`. Fixed by Astroid so the rate
7
7
  * rules, the middleware, and the portal routes can't drift apart.
8
8
  */
@@ -15,7 +15,7 @@ export declare const ASTROID_CHECKOUT_PATH = "/api/checkout";
15
15
  * checkout when commerce is configured.
16
16
  *
17
17
  * Rules are matched first-wins, and `security.rateRules` from the config are
18
- * placed FIRST — so a site tightens or loosens any default by declaring its own
18
+ * placed FIRST—so a site tightens or loosens any default by declaring its own
19
19
  * rule for that path, rather than losing the whole set to override one budget.
20
20
  */
21
21
  export declare function astroidRateRules(config: AstroidConfig): RateRule[];
@@ -5,26 +5,26 @@
5
5
  // The limiter mechanism lives in `louise-toolkit/security` and is deliberately
6
6
  // unopinionated: which routes, and which budgets, are policy. But the policy
7
7
  // turned out not to vary. All three consuming sites independently wrote the same
8
- // `RateRule[]` — the same public POST surfaces, the same 10-minute windows,
9
- // budgets within a factor of one of each other — differing only where the site
8
+ // `RateRule[]`—the same public POST surfaces, the same 10-minute windows,
9
+ // budgets within a factor of one of each other—differing only where the site
10
10
  // had a surface the others didn't (a portal, a checkout). That is a default, not
11
11
  // a per-site decision, so Astroid derives the whole set from the config.
12
12
  //
13
13
  // What's in scope: the public, UNAUTHENTICATED POST surfaces. Editor endpoints
14
- // (`/api/louise/*`) are session-gated and stay out on purpose — a limiter that
14
+ // (`/api/louise/*`) are session-gated and stay out on purpose—a limiter that
15
15
  // can lock the owner out of their own studio is worse than the abuse it stops.
16
16
  // The contact form is also absent by design: it's a worker route with its own
17
17
  // per-form limiter, and worker routes are matched before Astro's middleware ever
18
18
  // runs, so a rule here would never fire.
19
19
  /**
20
- * Base path the customer/portal Better Auth instance mounts at — its own
20
+ * Base path the customer/portal Better Auth instance mounts at—its own
21
21
  * handler, separate from the editor's `/api/auth`. Fixed by Astroid so the rate
22
22
  * rules, the middleware, and the portal routes can't drift apart.
23
23
  */
24
24
  export const ASTROID_PORTAL_BASE_PATH = "/api/portal-auth";
25
25
  /** Path the commerce module's checkout POSTs to. */
26
26
  export const ASTROID_CHECKOUT_PATH = "/api/checkout";
27
- /** Ten minutes. Every default budget uses this window — long enough that a
27
+ /** Ten minutes. Every default budget uses this window—long enough that a
28
28
  * burst can't wait it out, short enough that a false positive self-heals. */
29
29
  const WINDOW = 600;
30
30
  const exact = (path) => (p) => p === path;
@@ -34,7 +34,7 @@ const exact = (path) => (p) => p === path;
34
34
  * checkout when commerce is configured.
35
35
  *
36
36
  * Rules are matched first-wins, and `security.rateRules` from the config are
37
- * placed FIRST — so a site tightens or loosens any default by declaring its own
37
+ * placed FIRST—so a site tightens or loosens any default by declaring its own
38
38
  * rule for that path, rather than losing the whole set to override one budget.
39
39
  */
40
40
  export function astroidRateRules(config) {
@@ -62,9 +62,9 @@ export function astroidRateRules(config) {
62
62
  if (config.portal?.enabled) {
63
63
  // The portal's mount is configurable (a site may already ship a second
64
64
  // instance at its own path), so the credential-surface rules must track the
65
- // resolved base path, not the default constant — mirrors `astroidPortal`.
65
+ // resolved base path, not the default constant—mirrors `astroidPortal`.
66
66
  const base = config.portal.basePath ?? ASTROID_PORTAL_BASE_PATH;
67
- // Customer credentials, unlike the editor's, are password-based — so these
67
+ // Customer credentials, unlike the editor's, are password-based—so these
68
68
  // guard credential stuffing and enumeration, not just mail volume.
69
69
  rules.push({
70
70
  name: "portal-signup",
@@ -8,7 +8,7 @@ export interface AstroidSeoSettings {
8
8
  tagline?: string | null;
9
9
  metaDescription?: string | null;
10
10
  defaultOgImageUrl?: string | null;
11
- /** Site-wide kill switch — noindex every page (staging, pre-launch). */
11
+ /** Site-wide kill switch—noindex every page (staging, pre-launch). */
12
12
  disableIndexing?: boolean | null;
13
13
  }
14
14
  /** Per-page SEO: an editor's overrides, or a built-in page's own defaults. */
@@ -24,7 +24,7 @@ export interface PageSeoInput {
24
24
  noindex?: boolean | null;
25
25
  }
26
26
  export interface AstroidSeoOptions {
27
- /** Canonical URL of the page being rendered — absolute. */
27
+ /** Canonical URL of the page being rendered—absolute. */
28
28
  canonical: string;
29
29
  /**
30
30
  * Title template, `%s` standing in for the page title. Applied ONLY when the
@@ -33,15 +33,15 @@ export interface AstroidSeoOptions {
33
33
  titleTemplate?: string;
34
34
  /** `@<handle>` for Twitter/X card attribution. */
35
35
  twitterHandle?: string;
36
- /** OG locale, e.g. `"en_US"`. */
36
+ /** OG locale, for example, `"en_US"`. */
37
37
  locale?: string;
38
38
  }
39
39
  /** Everything a `<head>` needs, fully resolved and absolute. */
40
40
  export interface ResolvedSeo {
41
- /** Final `<title>` — templated when the page supplied one. */
41
+ /** Final `<title>`—templated when the page supplied one. */
42
42
  title: string;
43
43
  /** Untemplated page title, for OG/Twitter (which shouldn't carry the site
44
- * suffix twice — the OG `site_name` already says it). */
44
+ * suffix twice—the OG `site_name` already says it). */
45
45
  bareTitle: string;
46
46
  description?: string;
47
47
  canonical: string;