@glw907/cairn-cms 0.60.1 → 0.62.1

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 (254) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/components/AdminLayout.svelte +22 -0
  3. package/dist/components/CairnAdmin.svelte +3 -0
  4. package/dist/components/CairnTidySettings.svelte +2 -2
  5. package/dist/components/CairnTidySettings.svelte.d.ts +1 -1
  6. package/dist/components/EditPage.svelte +116 -39
  7. package/dist/components/HelpHome.svelte +824 -0
  8. package/dist/components/HelpHome.svelte.d.ts +22 -0
  9. package/dist/components/MarkdownHelpDialog.svelte +4 -15
  10. package/dist/components/client-ingest.d.ts +16 -8
  11. package/dist/components/client-ingest.js +12 -6
  12. package/dist/components/editor-media.js +16 -8
  13. package/dist/components/editor-placeholder.d.ts +4 -2
  14. package/dist/components/editor-tidy.d.ts +24 -12
  15. package/dist/components/editor-tidy.js +8 -4
  16. package/dist/components/index.d.ts +1 -0
  17. package/dist/components/index.js +1 -0
  18. package/dist/components/link-completion.d.ts +12 -6
  19. package/dist/components/link-completion.js +12 -6
  20. package/dist/components/markdown-directives.d.ts +9 -6
  21. package/dist/components/markdown-directives.js +9 -6
  22. package/dist/components/markdown-format.d.ts +7 -2
  23. package/dist/components/markdown-format.js +59 -28
  24. package/dist/components/markdown-reference.d.ts +8 -0
  25. package/dist/components/markdown-reference.js +22 -0
  26. package/dist/components/media-upload-outcome.d.ts +12 -6
  27. package/dist/components/objective-errors.d.ts +8 -4
  28. package/dist/components/objective-errors.js +8 -4
  29. package/dist/components/preview-doc.d.ts +4 -2
  30. package/dist/components/preview-doc.js +4 -2
  31. package/dist/components/spellcheck.d.ts +55 -29
  32. package/dist/components/spellcheck.js +39 -21
  33. package/dist/components/tidy-categorize.d.ts +20 -10
  34. package/dist/components/tidy-categorize.js +16 -8
  35. package/dist/components/tidy-validate.d.ts +12 -6
  36. package/dist/components/tidy-validate.js +20 -10
  37. package/dist/components/topbar-context.d.ts +4 -2
  38. package/dist/content/advisories.d.ts +51 -0
  39. package/dist/content/advisories.js +79 -0
  40. package/dist/content/compose.d.ts +4 -2
  41. package/dist/content/compose.js +1 -0
  42. package/dist/content/excerpt.js +4 -2
  43. package/dist/content/getting-started.d.ts +18 -0
  44. package/dist/content/getting-started.js +12 -0
  45. package/dist/content/links.d.ts +16 -8
  46. package/dist/content/links.js +12 -6
  47. package/dist/content/manifest.d.ts +36 -18
  48. package/dist/content/manifest.js +32 -16
  49. package/dist/content/media-refs.d.ts +4 -2
  50. package/dist/content/media-refs.js +4 -2
  51. package/dist/content/media-rewrite.d.ts +8 -4
  52. package/dist/content/media-rewrite.js +76 -38
  53. package/dist/content/schema.d.ts +20 -10
  54. package/dist/content/site-dictionary.d.ts +4 -2
  55. package/dist/content/site-dictionary.js +8 -4
  56. package/dist/content/types.d.ts +97 -42
  57. package/dist/delivery/content-index.d.ts +16 -8
  58. package/dist/delivery/feeds.js +4 -2
  59. package/dist/delivery/json-ld.d.ts +3 -0
  60. package/dist/delivery/json-ld.js +3 -0
  61. package/dist/delivery/manifest.d.ts +4 -2
  62. package/dist/delivery/manifest.js +4 -2
  63. package/dist/delivery/public-routes.d.ts +12 -6
  64. package/dist/delivery/public-routes.js +4 -2
  65. package/dist/delivery/seo-fields.d.ts +12 -6
  66. package/dist/delivery/seo-fields.js +8 -4
  67. package/dist/delivery/site-indexes.d.ts +4 -2
  68. package/dist/delivery/site-resolver.d.ts +4 -2
  69. package/dist/delivery/site-resolver.js +4 -2
  70. package/dist/doctor/cloudflare-api.d.ts +6 -0
  71. package/dist/doctor/cloudflare-api.js +6 -0
  72. package/dist/doctor/index.d.ts +12 -6
  73. package/dist/doctor/report.d.ts +3 -0
  74. package/dist/doctor/report.js +3 -0
  75. package/dist/doctor/run.d.ts +3 -0
  76. package/dist/doctor/run.js +3 -0
  77. package/dist/doctor/types.d.ts +10 -2
  78. package/dist/doctor/types.js +6 -0
  79. package/dist/doctor/wrangler-config.d.ts +7 -2
  80. package/dist/doctor/wrangler-config.js +3 -0
  81. package/dist/email.d.ts +4 -2
  82. package/dist/env.d.ts +0 -3
  83. package/dist/env.js +0 -3
  84. package/dist/github/branches.d.ts +4 -2
  85. package/dist/github/branches.js +4 -2
  86. package/dist/github/signing.d.ts +1 -1
  87. package/dist/github/signing.js +2 -2
  88. package/dist/log/events.d.ts +1 -1
  89. package/dist/media/bulk-delete-plan.d.ts +8 -4
  90. package/dist/media/config.d.ts +12 -6
  91. package/dist/media/config.js +16 -8
  92. package/dist/media/delivery-bucket.d.ts +4 -2
  93. package/dist/media/library-entry.d.ts +4 -2
  94. package/dist/media/library-entry.js +4 -2
  95. package/dist/media/manifest.d.ts +29 -15
  96. package/dist/media/manifest.js +29 -16
  97. package/dist/media/naming.d.ts +12 -6
  98. package/dist/media/naming.js +24 -12
  99. package/dist/media/orphan-scan.d.ts +4 -2
  100. package/dist/media/reconcile.d.ts +21 -11
  101. package/dist/media/reconcile.js +12 -6
  102. package/dist/media/reference.d.ts +8 -4
  103. package/dist/media/reference.js +12 -6
  104. package/dist/media/rewrite-plan.d.ts +12 -6
  105. package/dist/media/sniff.d.ts +4 -2
  106. package/dist/media/sniff.js +28 -14
  107. package/dist/media/store.d.ts +16 -8
  108. package/dist/media/store.js +4 -2
  109. package/dist/media/transform-url.d.ts +12 -6
  110. package/dist/media/transform-url.js +8 -4
  111. package/dist/media/usage.d.ts +8 -4
  112. package/dist/nav/site-config.d.ts +16 -8
  113. package/dist/render/component-grammar.d.ts +23 -10
  114. package/dist/render/component-grammar.js +19 -8
  115. package/dist/render/component-insert.d.ts +8 -4
  116. package/dist/render/component-insert.js +4 -2
  117. package/dist/render/component-reference.d.ts +4 -2
  118. package/dist/render/component-reference.js +4 -2
  119. package/dist/render/component-validate.d.ts +3 -0
  120. package/dist/render/component-validate.js +3 -0
  121. package/dist/render/glyph.d.ts +4 -2
  122. package/dist/render/glyph.js +4 -2
  123. package/dist/render/pipeline.d.ts +20 -10
  124. package/dist/render/pipeline.js +4 -2
  125. package/dist/render/registry.d.ts +40 -20
  126. package/dist/render/registry.js +16 -8
  127. package/dist/render/rehype-dispatch.d.ts +22 -8
  128. package/dist/render/rehype-dispatch.js +22 -8
  129. package/dist/render/remark-directives.d.ts +3 -0
  130. package/dist/render/remark-directives.js +3 -0
  131. package/dist/render/remark-figure.d.ts +4 -2
  132. package/dist/render/remark-figure.js +4 -2
  133. package/dist/render/resolve-links.d.ts +4 -2
  134. package/dist/render/resolve-links.js +4 -2
  135. package/dist/render/resolve-media.d.ts +16 -8
  136. package/dist/render/resolve-media.js +12 -6
  137. package/dist/sveltekit/admin-dispatch.d.ts +2 -0
  138. package/dist/sveltekit/admin-dispatch.js +9 -3
  139. package/dist/sveltekit/auth-routes.d.ts +3 -0
  140. package/dist/sveltekit/auth-routes.js +3 -0
  141. package/dist/sveltekit/cairn-admin.d.ts +16 -5
  142. package/dist/sveltekit/cairn-admin.js +26 -10
  143. package/dist/sveltekit/content-routes.d.ts +191 -86
  144. package/dist/sveltekit/content-routes.js +295 -107
  145. package/dist/sveltekit/editors-routes.d.ts +3 -0
  146. package/dist/sveltekit/editors-routes.js +3 -0
  147. package/dist/sveltekit/guard.d.ts +4 -2
  148. package/dist/sveltekit/guard.js +4 -2
  149. package/dist/sveltekit/https-required-page.d.ts +1 -1
  150. package/dist/sveltekit/https-required-page.js +1 -1
  151. package/dist/sveltekit/index.d.ts +1 -1
  152. package/dist/sveltekit/media-route.d.ts +1 -2
  153. package/dist/sveltekit/media-route.js +13 -8
  154. package/dist/sveltekit/nav-routes.d.ts +7 -2
  155. package/dist/sveltekit/nav-routes.js +3 -0
  156. package/dist/sveltekit/types.d.ts +4 -2
  157. package/dist/vite/index.d.ts +32 -16
  158. package/dist/vite/index.js +52 -26
  159. package/dist/vite/resolve-root.d.ts +8 -4
  160. package/dist/vite/resolve-root.js +4 -2
  161. package/package.json +7 -1
  162. package/src/lib/components/AdminLayout.svelte +22 -0
  163. package/src/lib/components/CairnAdmin.svelte +3 -0
  164. package/src/lib/components/CairnTidySettings.svelte +2 -2
  165. package/src/lib/components/ComponentForm.svelte +0 -1
  166. package/src/lib/components/EditPage.svelte +133 -41
  167. package/src/lib/components/HelpHome.svelte +850 -0
  168. package/src/lib/components/MarkdownHelpDialog.svelte +4 -15
  169. package/src/lib/components/client-ingest.ts +20 -10
  170. package/src/lib/components/editor-media.ts +20 -10
  171. package/src/lib/components/editor-placeholder.ts +12 -6
  172. package/src/lib/components/editor-tidy.ts +28 -14
  173. package/src/lib/components/index.ts +1 -0
  174. package/src/lib/components/link-completion.ts +12 -6
  175. package/src/lib/components/markdown-directives.ts +13 -8
  176. package/src/lib/components/markdown-format.ts +63 -30
  177. package/src/lib/components/markdown-reference.ts +30 -0
  178. package/src/lib/components/media-upload-outcome.ts +12 -6
  179. package/src/lib/components/objective-errors.ts +16 -8
  180. package/src/lib/components/preview-doc.ts +4 -2
  181. package/src/lib/components/spellcheck.ts +79 -41
  182. package/src/lib/components/tidy-categorize.ts +28 -14
  183. package/src/lib/components/tidy-validate.ts +28 -14
  184. package/src/lib/components/topbar-context.ts +4 -2
  185. package/src/lib/content/advisories.ts +141 -0
  186. package/src/lib/content/compose.ts +5 -2
  187. package/src/lib/content/excerpt.ts +4 -2
  188. package/src/lib/content/getting-started.ts +31 -0
  189. package/src/lib/content/links.ts +16 -8
  190. package/src/lib/content/manifest.ts +36 -18
  191. package/src/lib/content/media-refs.ts +4 -2
  192. package/src/lib/content/media-rewrite.ts +100 -50
  193. package/src/lib/content/schema.ts +20 -10
  194. package/src/lib/content/site-dictionary.ts +8 -4
  195. package/src/lib/content/types.ts +97 -42
  196. package/src/lib/delivery/content-index.ts +16 -8
  197. package/src/lib/delivery/feeds.ts +4 -2
  198. package/src/lib/delivery/json-ld.ts +3 -0
  199. package/src/lib/delivery/manifest.ts +4 -2
  200. package/src/lib/delivery/public-routes.ts +16 -8
  201. package/src/lib/delivery/seo-fields.ts +12 -6
  202. package/src/lib/delivery/site-indexes.ts +4 -2
  203. package/src/lib/delivery/site-resolver.ts +4 -2
  204. package/src/lib/doctor/cloudflare-api.ts +6 -0
  205. package/src/lib/doctor/index.ts +12 -6
  206. package/src/lib/doctor/report.ts +3 -0
  207. package/src/lib/doctor/run.ts +3 -0
  208. package/src/lib/doctor/types.ts +10 -2
  209. package/src/lib/doctor/wrangler-config.ts +7 -2
  210. package/src/lib/email.ts +4 -2
  211. package/src/lib/env.ts +0 -3
  212. package/src/lib/github/branches.ts +4 -2
  213. package/src/lib/github/signing.ts +2 -2
  214. package/src/lib/log/events.ts +1 -0
  215. package/src/lib/media/bulk-delete-plan.ts +8 -4
  216. package/src/lib/media/config.ts +24 -12
  217. package/src/lib/media/delivery-bucket.ts +4 -2
  218. package/src/lib/media/library-entry.ts +4 -2
  219. package/src/lib/media/manifest.ts +33 -18
  220. package/src/lib/media/naming.ts +24 -12
  221. package/src/lib/media/orphan-scan.ts +4 -2
  222. package/src/lib/media/reconcile.ts +21 -11
  223. package/src/lib/media/reference.ts +12 -6
  224. package/src/lib/media/rewrite-plan.ts +12 -6
  225. package/src/lib/media/sniff.ts +28 -14
  226. package/src/lib/media/store.ts +16 -8
  227. package/src/lib/media/transform-url.ts +12 -6
  228. package/src/lib/media/usage.ts +8 -4
  229. package/src/lib/nav/site-config.ts +16 -8
  230. package/src/lib/render/component-grammar.ts +23 -10
  231. package/src/lib/render/component-insert.ts +8 -4
  232. package/src/lib/render/component-reference.ts +4 -2
  233. package/src/lib/render/component-validate.ts +3 -0
  234. package/src/lib/render/glyph.ts +4 -2
  235. package/src/lib/render/pipeline.ts +20 -10
  236. package/src/lib/render/registry.ts +44 -22
  237. package/src/lib/render/rehype-dispatch.ts +22 -8
  238. package/src/lib/render/remark-directives.ts +3 -0
  239. package/src/lib/render/remark-figure.ts +4 -2
  240. package/src/lib/render/resolve-links.ts +4 -2
  241. package/src/lib/render/resolve-media.ts +16 -8
  242. package/src/lib/sveltekit/admin-dispatch.ts +10 -4
  243. package/src/lib/sveltekit/auth-routes.ts +3 -0
  244. package/src/lib/sveltekit/cairn-admin.ts +37 -15
  245. package/src/lib/sveltekit/content-routes.ts +492 -197
  246. package/src/lib/sveltekit/editors-routes.ts +3 -0
  247. package/src/lib/sveltekit/guard.ts +4 -2
  248. package/src/lib/sveltekit/https-required-page.ts +1 -1
  249. package/src/lib/sveltekit/index.ts +3 -0
  250. package/src/lib/sveltekit/media-route.ts +13 -8
  251. package/src/lib/sveltekit/nav-routes.ts +7 -2
  252. package/src/lib/sveltekit/types.ts +4 -2
  253. package/src/lib/vite/index.ts +60 -30
  254. package/src/lib/vite/resolve-root.ts +8 -4
@@ -23,6 +23,9 @@ function parseRole(value: FormDataEntryValue | null): Role {
23
23
  return value === 'owner' ? 'owner' : 'editor';
24
24
  }
25
25
 
26
+ /**
27
+ *
28
+ */
26
29
  export function createEditorRoutes() {
27
30
  /** GET /admin/editors. Owner-only. Returns the allowlist and the acting owner's email. */
28
31
  async function editorsLoad(event: RequestContext): Promise<{ editors: Editor[]; self: string }> {
@@ -102,9 +102,11 @@ export function createAuthGuard() {
102
102
  };
103
103
  }
104
104
 
105
- /** For a protected load/action: the session the guard already resolved, or a login redirect.
105
+ /**
106
+ * For a protected load/action: the session the guard already resolved, or a login redirect.
106
107
  * The parameter is the minimal structural need (just `locals`), so every engine event shape
107
- * (RequestContext, the content routes' ContentEvent) and a real RequestEvent all satisfy it. */
108
+ * (RequestContext, the content routes' ContentEvent) and a real RequestEvent all satisfy it.
109
+ */
108
110
  export function requireSession(event: { locals: { editor?: Editor | null } }): Editor {
109
111
  const editor = event.locals.editor;
110
112
  if (!editor) throw redirect(303, '/admin/login');
@@ -9,7 +9,7 @@ import { renderStaticAdminPage } from './static-admin-page.js';
9
9
 
10
10
  /**
11
11
  * Render the full HTML document for the HTTPS-required page.
12
- * @param httpsUrl The same request rebuilt over https, offered as the one-click recovery link.
12
+ * @param httpsUrl - The same request rebuilt over https, offered as the one-click recovery link.
13
13
  */
14
14
  export function httpsRequiredPage(httpsUrl: string): string {
15
15
  const href = escapeHtml(httpsUrl);
@@ -11,6 +11,9 @@ export type {
11
11
  EntrySummary,
12
12
  ListData,
13
13
  EditData,
14
+ AdvisoryNotice,
15
+ AdvisoryAction,
16
+ HelpData,
14
17
  MediaUsageInfo,
15
18
  MediaLibraryData,
16
19
  ContentEvent,
@@ -17,13 +17,17 @@ import type { ResolvedAssetConfig } from '../media/config.js';
17
17
  /** A 16-character lowercase hex content-hash prefix, validated before any R2 lookup. */
18
18
  const HASH_RE = /^[0-9a-f]{16}$/;
19
19
 
20
- /** The closed delivery extension allow-list. A filename ext outside this set is a 404 with no R2
21
- * read, so the route can never serve a type it cannot vouch for. */
20
+ /**
21
+ * The closed delivery extension allow-list. A filename ext outside this set is a 404 with no R2
22
+ * read, so the route can never serve a type it cannot vouch for.
23
+ */
22
24
  const DELIVERY_EXTS: ReadonlySet<string> = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif']);
23
25
 
24
- /** The load-bearing XSS control: set on every non-404 response, so a served object can never run as
26
+ /**
27
+ * The load-bearing XSS control: set on every non-404 response, so a served object can never run as
25
28
  * active content. `Content-Type` comes from the stored, server-validated metadata via
26
- * `writeHttpMetadata`; these override or add to it. */
29
+ * `writeHttpMetadata`; these override or add to it.
30
+ */
27
31
  function applySecurityHeaders(headers: Headers, etag: string): void {
28
32
  headers.set('X-Content-Type-Options', 'nosniff');
29
33
  headers.set('Content-Disposition', 'inline');
@@ -36,8 +40,10 @@ function applySecurityHeaders(headers: Headers, etag: string): void {
36
40
  if (!headers.has('Content-Type')) headers.set('Content-Type', 'application/octet-stream');
37
41
  }
38
42
 
39
- /** True when the returned object carries a body (a full or ranged read), narrowing it to the body
40
- * variant. R2 returns a body-less object on an `If-None-Match` hit. */
43
+ /**
44
+ * True when the returned object carries a body (a full or ranged read), narrowing it to the body
45
+ * variant. R2 returns a body-less object on an `If-None-Match` hit.
46
+ */
41
47
  function hasBody(obj: DeliveryObject | DeliveryObjectBody): obj is DeliveryObjectBody {
42
48
  return 'body' in obj && (obj as DeliveryObjectBody).body != null;
43
49
  }
@@ -48,8 +54,7 @@ function hasBody(obj: DeliveryObject | DeliveryObjectBody): obj is DeliveryObjec
48
54
  * The handler validates the hash and extension before any R2 call, derives the object key from the
49
55
  * validated values only (never trusting the URL's fan-out), guards the Cloudflare Images self-loop,
50
56
  * and sets the security headers on every served response.
51
- *
52
- * @param resolved the adapter's resolved media config; when media is off the handler always 404s.
57
+ * @param resolved - the adapter's resolved media config; when media is off the handler always 404s.
53
58
  */
54
59
  export function createMediaRoute(resolved: ResolvedAssetConfig): RequestHandler {
55
60
  return async (event) => {
@@ -29,11 +29,16 @@ export interface NavLoadData {
29
29
 
30
30
  /** Injectable dependencies; tests stub the token mint to avoid signing a real key. */
31
31
  export interface NavRoutesDeps {
32
- /** Mint a GitHub App installation token from the Worker env. Defaults to the real signer.
33
- * A bare string works too; the routes await whatever comes back. */
32
+ /**
33
+ * Mint a GitHub App installation token from the Worker env. Defaults to the real signer.
34
+ * A bare string works too; the routes await whatever comes back.
35
+ */
34
36
  mintToken?: (env: GithubKeyEnv) => string | Promise<string>;
35
37
  }
36
38
 
39
+ /**
40
+ *
41
+ */
37
42
  export function createNavRoutes(runtime: CairnRuntime, deps: NavRoutesDeps = {}) {
38
43
  const mintToken =
39
44
  deps.mintToken ?? ((env: GithubKeyEnv) => cachedInstallationToken(appCredentials(runtime.backend, env)));
@@ -23,9 +23,11 @@ export interface PlatformContext<Env> {
23
23
  context?: { waitUntil(promise: Promise<unknown>): void };
24
24
  }
25
25
 
26
- /** The structural core every engine event type extends, parameterized by the Worker env the
26
+ /**
27
+ * The structural core every engine event type extends, parameterized by the Worker env the
27
28
  * surface reads. Each shared field is defined once here; the extensions add only what their
28
- * surface needs (cookies, params, setHeaders). */
29
+ * surface needs (cookies, params, setHeaders).
30
+ */
29
31
  export interface EventBase<Env> {
30
32
  url: URL;
31
33
  request: Request;
@@ -10,15 +10,19 @@ import { writeFile, mkdir } from 'node:fs/promises';
10
10
  import { dirname, join } from 'node:path';
11
11
  import { resolveViteRoot } from './resolve-root.js';
12
12
 
13
- /** The key the cairnManifest plugin stashes its options under, so the write path can read them off the
14
- * plugin instance in the consumer's loaded config without re-parsing the config file. */
13
+ /**
14
+ * The key the cairnManifest plugin stashes its options under, so the write path can read them off the
15
+ * plugin instance in the consumer's loaded config without re-parsing the config file.
16
+ */
15
17
  const CAIRN_OPTIONS = Symbol.for('cairn-cms.manifest-options');
16
18
 
17
19
  /** A cairnManifest plugin instance with its options stashed for the write path to read. */
18
20
  type CairnManifestPlugin = Plugin & { [CAIRN_OPTIONS]?: CairnManifestOptions };
19
21
 
20
- /** Options for {@link cairnManifest}. Paths are app-root-absolute (the form `import.meta.glob` wants),
21
- * so they match the build's own resolution. */
22
+ /**
23
+ * Options for {@link cairnManifest}. Paths are app-root-absolute (the form `import.meta.glob` wants),
24
+ * so they match the build's own resolution.
25
+ */
22
26
  export interface CairnManifestOptions {
23
27
  /** The module exporting the `cairn` adapter and the parsed `siteConfig`, app-root-absolute. */
24
28
  configModule: string;
@@ -34,9 +38,11 @@ const RESOLVED_ID = '\0' + VIRTUAL_ID;
34
38
  /** The default committed manifest path, app-root-absolute. */
35
39
  const DEFAULT_MANIFEST_PATH = '/src/content/.cairn/index.json';
36
40
 
37
- /** Build the virtual module source. In verify mode it throws on drift; in write mode it exports the
41
+ /**
42
+ * Build the virtual module source. In verify mode it throws on drift; in write mode it exports the
38
43
  * serialized manifest as `result`. The module runs in the app graph, so its `import.meta.glob`,
39
- * package, and `?raw` resolution is the build's own. */
44
+ * package, and `?raw` resolution is the build's own.
45
+ */
40
46
  function virtualSource(opts: CairnManifestOptions, mode: 'verify' | 'write'): string {
41
47
  const manifestPath = opts.manifestPath ?? DEFAULT_MANIFEST_PATH;
42
48
  const globEntries = Object.entries(opts.content)
@@ -62,11 +68,13 @@ export const result = ${resultExpr};
62
68
  `;
63
69
  }
64
70
 
65
- /** Evaluate a virtual module source inside the consumer's own Vite resolution, then return the
71
+ /**
72
+ * Evaluate a virtual module source inside the consumer's own Vite resolution, then return the
66
73
  * module's `result`. It reuses the consumer's loaded config (so `$lib`, the config module,
67
74
  * `import.meta.glob`, and `?raw` resolve exactly as the build does) and strips the cairnManifest
68
75
  * plugin from the nested server's plugin list, so its buildStart never recurses. This runs at
69
- * build time and in the bins, never in the request lifecycle. */
76
+ * build time and in the bins, never in the request lifecycle.
77
+ */
70
78
  async function evalVirtual(source: string, root: string): Promise<string> {
71
79
  const { createServer, loadConfigFromFile } = await import('vite');
72
80
  // Load the consumer's real Vite config so the nested server inherits SvelteKit's resolution
@@ -91,38 +99,48 @@ async function evalVirtual(source: string, root: string): Promise<string> {
91
99
  }
92
100
  }
93
101
 
94
- /** True for any plugin object whose name is the cairnManifest plugin, so the nested server drops it
102
+ /**
103
+ * True for any plugin object whose name is the cairnManifest plugin, so the nested server drops it
95
104
  * and cannot recurse into another buildStart. The consumer's plugin list may nest arrays and hold
96
- * falsy slots, so guard the shape. */
105
+ * falsy slots, so guard the shape.
106
+ */
97
107
  function isCairnManifestPlugin(p: unknown): boolean {
98
108
  return !!p && typeof p === 'object' && 'name' in p && (p as { name?: unknown }).name === 'cairn-manifest';
99
109
  }
100
110
 
101
- /** Flatten the consumer's plugins option and drop the cairnManifest plugin at any nesting depth, so
111
+ /**
112
+ * Flatten the consumer's plugins option and drop the cairnManifest plugin at any nesting depth, so
102
113
  * the nested verify server can never re-enter its buildStart. Vite supports (and flattens) nested
103
114
  * plugin arrays, and findCairnOptions recurses into them, so a flat single-level filter would miss a
104
115
  * cairnManifest nested inside a shared preset's sub-array and let it survive into the nested server.
105
- * This mirrors findCairnOptions's recursion. Falsy slots pass through, which Vite tolerates. */
116
+ * This mirrors findCairnOptions's recursion. Falsy slots pass through, which Vite tolerates.
117
+ */
106
118
  export function stripCairnManifest(plugins: PluginOption | PluginOption[]): PluginOption[] {
107
119
  if (Array.isArray(plugins)) return plugins.flatMap(stripCairnManifest);
108
120
  if (isCairnManifestPlugin(plugins)) return [];
109
121
  return [plugins];
110
122
  }
111
123
 
112
- /** Verify the committed manifest against the corpus from a Vite context, throwing on drift. The bin
113
- * and the plugin share this; the spike proved it runs cleanly inside the consumer's config. */
124
+ /**
125
+ * Verify the committed manifest against the corpus from a Vite context, throwing on drift. The bin
126
+ * and the plugin share this; the spike proved it runs cleanly inside the consumer's config.
127
+ */
114
128
  export async function verifyManifestFromVite(opts: CairnManifestOptions, root: string): Promise<void> {
115
129
  await evalVirtual(virtualSource(opts, 'verify'), root);
116
130
  }
117
131
 
118
- /** Regenerate the serialized manifest from the corpus in a Vite context, sharing the build's
119
- * resolution. The cairn-manifest bin (a later task) will call this and write the result. */
132
+ /**
133
+ * Regenerate the serialized manifest from the corpus in a Vite context, sharing the build's
134
+ * resolution. The cairn-manifest bin (a later task) will call this and write the result.
135
+ */
120
136
  export async function buildManifestFromVite(opts: CairnManifestOptions, root: string): Promise<string> {
121
137
  return evalVirtual(virtualSource(opts, 'write'), root);
122
138
  }
123
139
 
124
- /** The cairnManifest plugin. It serves the verify virtual module to the app graph and, in
125
- * buildStart, evaluates it through a nested Vite SSR load so a manifest drift fails the build. */
140
+ /**
141
+ * The cairnManifest plugin. It serves the verify virtual module to the app graph and, in
142
+ * buildStart, evaluates it through a nested Vite SSR load so a manifest drift fails the build.
143
+ */
126
144
  export function cairnManifest(opts: CairnManifestOptions): Plugin {
127
145
  let root = process.cwd();
128
146
  const plugin: CairnManifestPlugin = {
@@ -152,13 +170,15 @@ export function cairnManifest(opts: CairnManifestOptions): Plugin {
152
170
  return plugin;
153
171
  }
154
172
 
155
- /** Regenerate the committed manifest from the consumer's corpus and write it to the configured
173
+ /**
174
+ * Regenerate the committed manifest from the consumer's corpus and write it to the configured
156
175
  * manifestPath. It searches for the consumer's Vite config from `cwd`, derives the authoritative
157
176
  * Vite root from the loaded config (so a configured `root` or a non-root cwd resolves correctly),
158
177
  * reads the cairnManifest plugin's options off the instance, evaluates the write-mode virtual
159
178
  * module through the build's own resolution, and writes the serialized manifest under the Vite
160
179
  * root. The cairn-manifest bin calls this; it is exported so the write logic is testable apart
161
- * from the CLI shell. */
180
+ * from the CLI shell.
181
+ */
162
182
  export async function writeManifest(cwd: string = process.cwd()): Promise<void> {
163
183
  const { loadConfigFromFile } = await import('vite');
164
184
  const loaded = await loadConfigFromFile({ command: 'build', mode: 'production' }, undefined, cwd);
@@ -181,8 +201,10 @@ export async function writeManifest(cwd: string = process.cwd()): Promise<void>
181
201
  await writeFile(outPath, serialized);
182
202
  }
183
203
 
184
- /** Walk a Vite plugins option (which may nest arrays, hold falsy slots, or be a thenable) and return
185
- * the stashed cairnManifest options from the first matching plugin, or null if there is none. */
204
+ /**
205
+ * Walk a Vite plugins option (which may nest arrays, hold falsy slots, or be a thenable) and return
206
+ * the stashed cairnManifest options from the first matching plugin, or null if there is none.
207
+ */
186
208
  function findCairnOptions(plugins: unknown): CairnManifestOptions | null {
187
209
  if (!plugins) return null;
188
210
  if (Array.isArray(plugins)) {
@@ -198,8 +220,10 @@ function findCairnOptions(plugins: unknown): CairnManifestOptions | null {
198
220
  return null;
199
221
  }
200
222
 
201
- /** A minimal plugin that serves only the given virtual module source, for the nested SSR load. It
202
- * carries no buildStart, so the nested server never recurses into the verify. */
223
+ /**
224
+ * A minimal plugin that serves only the given virtual module source, for the nested SSR load. It
225
+ * carries no buildStart, so the nested server never recurses into the verify.
226
+ */
203
227
  function cairnVirtualOnly(source: string): Plugin {
204
228
  return {
205
229
  name: 'cairn-manifest-virtual',
@@ -220,15 +244,19 @@ export interface AdapterFacts {
220
244
  repo?: string;
221
245
  /** `cairn.sender.from`. */
222
246
  from?: string;
223
- /** `cairn.assets.bucketBinding`, the media R2 binding name; undefined when the adapter declares no
224
- * assets. The doctor's conditional media-bucket check reads it. */
247
+ /**
248
+ * `cairn.assets.bucketBinding`, the media R2 binding name; undefined when the adapter declares no
249
+ * assets. The doctor's conditional media-bucket check reads it.
250
+ */
225
251
  mediaBucketBinding?: string;
226
252
  }
227
253
 
228
- /** Build the virtual module that reads only the adapter facts the doctor derives. It imports the
254
+ /**
255
+ * Build the virtual module that reads only the adapter facts the doctor derives. It imports the
229
256
  * configured config module and exports the string-typed `owner`, `repo`, `from`, and the media
230
257
  * `bucketBinding` as JSON, so nothing else of the adapter (least of all a secret) crosses the
231
- * boundary. */
258
+ * boundary.
259
+ */
232
260
  function adapterFactsSource(opts: CairnManifestOptions): string {
233
261
  return `
234
262
  import { cairn } from ${JSON.stringify(opts.configModule)};
@@ -244,12 +272,14 @@ export const result = JSON.stringify(facts);
244
272
  `;
245
273
  }
246
274
 
247
- /** Read `{ owner, repo, from }` off the consumer's adapter by evaluating a tiny virtual module
275
+ /**
276
+ * Read `{ owner, repo, from }` off the consumer's adapter by evaluating a tiny virtual module
248
277
  * through the consumer's own Vite resolution, the same machinery the cairn-manifest bin uses.
249
278
  * cairn-doctor calls this to fill inputs the operator did not pass. Derivation is best-effort:
250
279
  * any failure (no Vite config, no cairnManifest plugin, a config module that throws) returns
251
280
  * null, so the doctor degrades to flags instead of crashing. This runs only on the bin path,
252
- * never in a Worker. */
281
+ * never in a Worker.
282
+ */
253
283
  export async function readAdapterFacts(cwd: string = process.cwd()): Promise<AdapterFacts | null> {
254
284
  try {
255
285
  const { loadConfigFromFile } = await import('vite');
@@ -2,8 +2,10 @@
2
2
  // /vite surface (only src/lib/vite/index.ts is the package subpath; this sibling is internal).
3
3
  import { dirname, isAbsolute, resolve } from 'node:path';
4
4
 
5
- /** The shape of `loadConfigFromFile`'s result that the root derivation reads: the config file's own
6
- * path and its `root` field. Typed structurally so the helper is testable without a real load. */
5
+ /**
6
+ * The shape of `loadConfigFromFile`'s result that the root derivation reads: the config file's own
7
+ * path and its `root` field. Typed structurally so the helper is testable without a real load.
8
+ */
7
9
  export interface LoadedViteConfig {
8
10
  /** The resolved path of the config file Vite loaded. */
9
11
  path: string;
@@ -11,11 +13,13 @@ export interface LoadedViteConfig {
11
13
  config: { root?: string };
12
14
  }
13
15
 
14
- /** The authoritative Vite root for the manifest bin, derived from the loaded config the way Vite
16
+ /**
17
+ * The authoritative Vite root for the manifest bin, derived from the loaded config the way Vite
15
18
  * resolves a relative `root`: against the config file's own directory, not cwd. An absolute `root`
16
19
  * stands as given, and no `root` falls back to `cwd` (the directory the bin was run from). This
17
20
  * separates the config-search dir (cwd) from the Vite root, so a non-root cwd or a config that
18
- * sets `root` reads and writes the manifest under the real app root. */
21
+ * sets `root` reads and writes the manifest under the real app root.
22
+ */
19
23
  export function resolveViteRoot(loaded: LoadedViteConfig, cwd: string): string {
20
24
  const root = loaded.config.root;
21
25
  if (!root) return cwd;