blume 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
- package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
- package/dist/cli/chunk-43zcf4gz.js.map +36 -0
- package/dist/cli/chunk-4ejxbzee.js +39 -0
- package/dist/cli/chunk-4ejxbzee.js.map +10 -0
- package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
- package/dist/cli/chunk-6bm0xpgd.js +18 -0
- package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
- package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
- package/dist/cli/chunk-713mngdq.js.map +14 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
- package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
- package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
- package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
- package/dist/cli/chunk-q3wxxq39.js +126 -0
- package/dist/cli/chunk-q3wxxq39.js.map +11 -0
- package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
- package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
- package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
- package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +28 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +22 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +20 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +11 -2
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/components/content/api-field.d.ts +59 -0
- package/dist/types/components/content/api-page.d.ts +95 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/helpers.d.ts +137 -0
- package/dist/types/components/openapi/security.d.ts +85 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +196 -27
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/redirect-patterns.d.ts +144 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +558 -56
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +1 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/platforms/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +61 -22
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +24 -2
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +12 -6
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +13 -0
- package/dist/types/openapi/references.d.ts +14 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +111 -0
- package/dist/types/ratelimit/upstash.d.ts +21 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +13 -10
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +45 -5
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +60 -36
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +4 -0
- package/dist/types/seo/metatags.d.ts +20 -0
- package/docs/02-deployment.mdx +44 -6
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +38 -2
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +22 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +6 -4
- package/docs/configuration/index.mdx +63 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +69 -0
- package/docs/configuration/search.mdx +30 -2
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +107 -1
- package/docs/content/frontmatter.mdx +77 -1
- package/docs/content/i18n.mdx +40 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +4 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +28 -6
- package/docs/content/sources/contentful.mdx +32 -0
- package/docs/content/sources/custom.mdx +39 -0
- package/docs/content/sources/github-releases.mdx +31 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +30 -0
- package/docs/content/sources/remote-mdx.mdx +40 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +31 -0
- package/docs/content/syntax.mdx +81 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +30 -3
- package/docs/discoverability/index.mdx +3 -2
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +3 -3
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +106 -5
- package/docs/references/scalar.mdx +3 -1
- package/package.json +36 -1
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +5 -5
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +3 -3
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +4 -3
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +12 -5
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/ask-context.ts +73 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +24 -9
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +31 -9
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +49 -3
- package/src/ai/tar.ts +139 -12
- package/src/ai/write-skill.ts +97 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +133 -19
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +143 -14
- package/src/astro/module-types.ts +6 -0
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +364 -67
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +19 -0
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +13 -13
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/graph.ts +3 -1
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +14 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +2 -1
- package/src/blume-modules.d.ts +7 -0
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +25 -17
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/index.ts +5 -0
- package/src/cli/report-format.ts +11 -6
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -0
- package/src/components/colors.ts +19 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +62 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +100 -0
- package/src/components/content/api-page.ts +224 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +125 -18
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +145 -40
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +11 -4
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +124 -82
- package/src/components/layout/LanguageSwitcher.astro +50 -17
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +35 -9
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +78 -9
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +313 -50
- package/src/components/layout/Search.astro +64 -30
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/analytics.ts +98 -0
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/types.ts +20 -0
- package/src/components/layout/search/typesense.ts +5 -1
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +163 -13
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +63 -8
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +953 -13
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/base-path.ts +62 -16
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +203 -27
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +5 -2
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/footer.ts +26 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +39 -36
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/i18n.ts +31 -3
- package/src/core/includes.ts +20 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +16 -2
- package/src/core/locale-links.ts +5 -1
- package/src/core/localizable.ts +26 -0
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +71 -20
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +138 -10
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +342 -68
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +327 -91
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +1 -1
- package/src/core/ui-packs/ar.ts +43 -0
- package/src/core/ui-packs/bg.ts +45 -0
- package/src/core/ui-packs/bn.ts +43 -0
- package/src/core/ui-packs/ca.ts +46 -0
- package/src/core/ui-packs/cs.ts +44 -0
- package/src/core/ui-packs/da.ts +45 -0
- package/src/core/ui-packs/de.ts +46 -0
- package/src/core/ui-packs/el.ts +45 -0
- package/src/core/ui-packs/es.ts +46 -0
- package/src/core/ui-packs/fa.ts +45 -0
- package/src/core/ui-packs/fi.ts +45 -0
- package/src/core/ui-packs/fr.ts +46 -0
- package/src/core/ui-packs/he.ts +43 -0
- package/src/core/ui-packs/hi.ts +44 -0
- package/src/core/ui-packs/hr.ts +45 -0
- package/src/core/ui-packs/hu.ts +44 -0
- package/src/core/ui-packs/id.ts +45 -0
- package/src/core/ui-packs/it.ts +45 -0
- package/src/core/ui-packs/ja.ts +45 -0
- package/src/core/ui-packs/ko.ts +44 -0
- package/src/core/ui-packs/nl.ts +46 -0
- package/src/core/ui-packs/no.ts +44 -0
- package/src/core/ui-packs/pl.ts +45 -0
- package/src/core/ui-packs/pt-br.ts +46 -0
- package/src/core/ui-packs/pt.ts +45 -0
- package/src/core/ui-packs/ro.ts +45 -0
- package/src/core/ui-packs/ru.ts +45 -0
- package/src/core/ui-packs/sk.ts +44 -0
- package/src/core/ui-packs/sr.ts +45 -0
- package/src/core/ui-packs/sv.ts +44 -0
- package/src/core/ui-packs/th.ts +43 -0
- package/src/core/ui-packs/tr.ts +45 -0
- package/src/core/ui-packs/uk.ts +45 -0
- package/src/core/ui-packs/vi.ts +45 -0
- package/src/core/ui-packs/zh-tw.ts +43 -0
- package/src/core/ui-packs/zh.ts +43 -0
- package/src/core/variables.ts +103 -0
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +104 -14
- package/src/deploy/cloudflare-negotiation.ts +39 -17
- package/src/deploy/headers.ts +10 -5
- package/src/deploy/node-headers.ts +64 -10
- package/src/deploy/platforms/cloudflare.ts +44 -3
- package/src/deploy/platforms/netlify.ts +34 -1
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +38 -2
- package/src/deploy/redirects.ts +139 -36
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +20 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +35 -11
- package/src/markdown/math.ts +4 -4
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +136 -11
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +77 -36
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +46 -3
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +1 -1
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +229 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +30 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +5 -2
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +51 -7
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +201 -28
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +93 -28
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +15 -4
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +5 -0
- package/src/seo/jsonld.ts +7 -3
- package/src/seo/metatags.ts +67 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +56 -3
- package/src/theme/palette.ts +121 -45
- package/src/translate/meta.ts +68 -24
- package/src/translate/prompts.ts +1 -0
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +12 -3
- package/src/translate/work-list.ts +56 -17
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-3yce002v.js.map +0 -14
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-mqc662a6.js +0 -75
- package/dist/cli/chunk-mqc662a6.js.map +0 -11
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
- package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- package/dist/cli/chunk-xh43dwgw.js.map +0 -36
- package/docs/content/sources.mdx +0 -338
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/theme/color.ts", "../src/theme/palette.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Just enough CSS color math to check contrast at build time: parse the color\n * syntaxes a theme config takes (hex, `rgb()`, `hsl()`, `hwb()`, `lab()`,\n * `lch()`, `oklab()`, `oklch()`, and named colors) to sRGB, then measure WCAG\n * 2 relative luminance and contrast. A color outside sRGB is clipped to it,\n * which is what a browser shows on an sRGB display. Anything else — `var()`,\n * `color-mix()`, `color()` — parses to `null`, and the caller says it couldn't\n * check it rather than guessing.\n */\n\n/** An sRGB color: gamma-encoded channels and alpha, each 0–1. */\nexport interface Rgba {\n alpha: number;\n blue: number;\n green: number;\n red: number;\n}\n\n/** One parsed function argument. */\ninterface Component {\n /** `\"%\"`, an angle unit, or `\"\"` for a bare number. */\n unit: string;\n value: number;\n}\n\n/** A color function's three channel arguments. */\ntype Channels = readonly [Component, Component, Component];\n\n/** A parsed color function: its name and its channel and alpha arguments. */\ninterface ColorFunction {\n alpha?: Component;\n channels: Channels;\n name: string;\n}\n\n/** The CSS named colors, as hex. A `Map`, so `constructor` names nothing. */\nconst NAMED = new Map(\n Object.entries({\n aliceblue: \"f0f8ff\",\n antiquewhite: \"faebd7\",\n aqua: \"00ffff\",\n aquamarine: \"7fffd4\",\n azure: \"f0ffff\",\n beige: \"f5f5dc\",\n bisque: \"ffe4c4\",\n black: \"000000\",\n blanchedalmond: \"ffebcd\",\n blue: \"0000ff\",\n blueviolet: \"8a2be2\",\n brown: \"a52a2a\",\n burlywood: \"deb887\",\n cadetblue: \"5f9ea0\",\n chartreuse: \"7fff00\",\n chocolate: \"d2691e\",\n coral: \"ff7f50\",\n cornflowerblue: \"6495ed\",\n cornsilk: \"fff8dc\",\n crimson: \"dc143c\",\n cyan: \"00ffff\",\n darkblue: \"00008b\",\n darkcyan: \"008b8b\",\n darkgoldenrod: \"b8860b\",\n darkgray: \"a9a9a9\",\n darkgreen: \"006400\",\n darkgrey: \"a9a9a9\",\n darkkhaki: \"bdb76b\",\n darkmagenta: \"8b008b\",\n darkolivegreen: \"556b2f\",\n darkorange: \"ff8c00\",\n darkorchid: \"9932cc\",\n darkred: \"8b0000\",\n darksalmon: \"e9967a\",\n darkseagreen: \"8fbc8f\",\n darkslateblue: \"483d8b\",\n darkslategray: \"2f4f4f\",\n darkslategrey: \"2f4f4f\",\n darkturquoise: \"00ced1\",\n darkviolet: \"9400d3\",\n deeppink: \"ff1493\",\n deepskyblue: \"00bfff\",\n dimgray: \"696969\",\n dimgrey: \"696969\",\n dodgerblue: \"1e90ff\",\n firebrick: \"b22222\",\n floralwhite: \"fffaf0\",\n forestgreen: \"228b22\",\n fuchsia: \"ff00ff\",\n gainsboro: \"dcdcdc\",\n ghostwhite: \"f8f8ff\",\n gold: \"ffd700\",\n goldenrod: \"daa520\",\n gray: \"808080\",\n green: \"008000\",\n greenyellow: \"adff2f\",\n grey: \"808080\",\n honeydew: \"f0fff0\",\n hotpink: \"ff69b4\",\n indianred: \"cd5c5c\",\n indigo: \"4b0082\",\n ivory: \"fffff0\",\n khaki: \"f0e68c\",\n lavender: \"e6e6fa\",\n lavenderblush: \"fff0f5\",\n lawngreen: \"7cfc00\",\n lemonchiffon: \"fffacd\",\n lightblue: \"add8e6\",\n lightcoral: \"f08080\",\n lightcyan: \"e0ffff\",\n lightgoldenrodyellow: \"fafad2\",\n lightgray: \"d3d3d3\",\n lightgreen: \"90ee90\",\n lightgrey: \"d3d3d3\",\n lightpink: \"ffb6c1\",\n lightsalmon: \"ffa07a\",\n lightseagreen: \"20b2aa\",\n lightskyblue: \"87cefa\",\n lightslategray: \"778899\",\n lightslategrey: \"778899\",\n lightsteelblue: \"b0c4de\",\n lightyellow: \"ffffe0\",\n lime: \"00ff00\",\n limegreen: \"32cd32\",\n linen: \"faf0e6\",\n magenta: \"ff00ff\",\n maroon: \"800000\",\n mediumaquamarine: \"66cdaa\",\n mediumblue: \"0000cd\",\n mediumorchid: \"ba55d3\",\n mediumpurple: \"9370db\",\n mediumseagreen: \"3cb371\",\n mediumslateblue: \"7b68ee\",\n mediumspringgreen: \"00fa9a\",\n mediumturquoise: \"48d1cc\",\n mediumvioletred: \"c71585\",\n midnightblue: \"191970\",\n mintcream: \"f5fffa\",\n mistyrose: \"ffe4e1\",\n moccasin: \"ffe4b5\",\n navajowhite: \"ffdead\",\n navy: \"000080\",\n oldlace: \"fdf5e6\",\n olive: \"808000\",\n olivedrab: \"6b8e23\",\n orange: \"ffa500\",\n orangered: \"ff4500\",\n orchid: \"da70d6\",\n palegoldenrod: \"eee8aa\",\n palegreen: \"98fb98\",\n paleturquoise: \"afeeee\",\n palevioletred: \"db7093\",\n papayawhip: \"ffefd5\",\n peachpuff: \"ffdab9\",\n peru: \"cd853f\",\n pink: \"ffc0cb\",\n plum: \"dda0dd\",\n powderblue: \"b0e0e6\",\n purple: \"800080\",\n rebeccapurple: \"663399\",\n red: \"ff0000\",\n rosybrown: \"bc8f8f\",\n royalblue: \"4169e1\",\n saddlebrown: \"8b4513\",\n salmon: \"fa8072\",\n sandybrown: \"f4a460\",\n seagreen: \"2e8b57\",\n seashell: \"fff5ee\",\n sienna: \"a0522d\",\n silver: \"c0c0c0\",\n skyblue: \"87ceeb\",\n slateblue: \"6a5acd\",\n slategray: \"708090\",\n slategrey: \"708090\",\n snow: \"fffafa\",\n springgreen: \"00ff7f\",\n steelblue: \"4682b4\",\n tan: \"d2b48c\",\n teal: \"008080\",\n thistle: \"d8bfd8\",\n tomato: \"ff6347\",\n transparent: \"00000000\",\n turquoise: \"40e0d0\",\n violet: \"ee82ee\",\n wheat: \"f5deb3\",\n white: \"ffffff\",\n whitesmoke: \"f5f5f5\",\n yellow: \"ffff00\",\n yellowgreen: \"9acd32\",\n })\n);\n\nconst HEX = /^#(?<digits>[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$/iu;\nconst FUNCTION = /^(?<name>[a-z]+)\\((?<args>[^()]*)\\)$/iu;\nconst COMPONENT =\n /^(?<value>[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?)(?<unit>%|deg|grad|rad|turn)?$/iu;\n\n/** Turns per angle unit; a bare hue is in degrees. */\nconst TURNS_PER_UNIT = new Map([\n [\"\", 1 / 360],\n [\"deg\", 1 / 360],\n [\"grad\", 1 / 400],\n [\"rad\", 1 / (2 * Math.PI)],\n [\"turn\", 1],\n]);\n\nconst clamp = (value: number): number => Math.min(1, Math.max(0, value));\n\nconst fromHex = (digits: string): Rgba => {\n const full =\n digits.length <= 4\n ? [...digits].map((digit) => `${digit}${digit}`).join(\"\")\n : digits;\n const channel = (index: number): number =>\n Number.parseInt(full.slice(index * 2, index * 2 + 2), 16) / 255;\n return {\n alpha: full.length === 8 ? channel(3) : 1,\n blue: channel(2),\n green: channel(1),\n red: channel(0),\n };\n};\n\nconst parseComponent = (token: string): Component | null => {\n if (token.toLowerCase() === \"none\") {\n return { unit: \"\", value: 0 };\n }\n const match = COMPONENT.exec(token)?.groups;\n return match?.value === undefined\n ? null\n : { unit: (match.unit ?? \"\").toLowerCase(), value: Number(match.value) };\n};\n\n/** Split a function's arguments: commas or spaces, with `/` before alpha. */\nconst parseFunction = (value: string): ColorFunction | null => {\n const match = FUNCTION.exec(value)?.groups;\n if (match?.name === undefined || match.args === undefined) {\n return null;\n }\n const [channelText = \"\", alphaText, extra] = match.args.split(\"/\");\n const tokens = channelText\n .split(/[\\s,]+/u)\n .filter(Boolean)\n .map(parseComponent);\n // The legacy comma syntax (`rgba(0, 0, 0, 0.5)`) puts alpha fourth.\n const legacyAlpha =\n alphaText === undefined && tokens.length === 4 ? tokens.pop() : undefined;\n const alpha =\n alphaText === undefined ? legacyAlpha : parseComponent(alphaText.trim());\n const [first, second, third, ...rest] = tokens;\n if (\n extra !== undefined ||\n alpha === null ||\n rest.length > 0 ||\n !first ||\n !second ||\n !third\n ) {\n return null;\n }\n return {\n alpha,\n channels: [first, second, third],\n name: match.name.toLowerCase(),\n };\n};\n\n/** A number, or a percentage of `full`. */\nconst scaled = (component: Component, full: number): number =>\n component.unit === \"%\" ? (component.value / 100) * full : component.value;\n\n/** A hue in degrees (0–360), from any angle unit. */\nconst hueDegrees = (component: Component): number => {\n const degrees =\n component.value * (TURNS_PER_UNIT.get(component.unit) ?? 0) * 360;\n return ((degrees % 360) + 360) % 360;\n};\n\n/** An sRGB channel from its linear-light value. */\nconst encode = (linear: number): number =>\n clamp(\n linear <= 0.0031308 ? 12.92 * linear : 1.055 * linear ** (1 / 2.4) - 0.055\n );\n\n/** A linear-light value from its sRGB channel. */\nconst decode = (channel: number): number =>\n channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;\n\nconst fromLinear = (\n [red, green, blue]: readonly [number, number, number],\n alpha: number\n): Rgba => ({\n alpha,\n blue: encode(blue),\n green: encode(green),\n red: encode(red),\n});\n\nconst fromHsl = (hue: number, saturation: number, lightness: number) => {\n const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;\n const channel = (offset: number): number => {\n const k = (offset + hue / 30) % 12;\n return lightness - (chroma / 2) * Math.max(-1, Math.min(k - 3, 9 - k, 1));\n };\n return [channel(0), channel(8), channel(4)] as const;\n};\n\nconst fromHwb = (hue: number, white: number, black: number) => {\n if (white + black >= 1) {\n const gray = white / (white + black);\n return [gray, gray, gray] as const;\n }\n const [red, green, blue] = fromHsl(hue, 1, 0.5);\n const scale = 1 - white - black;\n return [\n red * scale + white,\n green * scale + white,\n blue * scale + white,\n ] as const;\n};\n\n/** Linear sRGB from OKLab (Björn Ottosson's matrices). */\nconst fromOklab = (lightness: number, a: number, b: number) => {\n const l = (lightness + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (lightness - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (lightness - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return [\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n ] as const;\n};\n\n/** Linear sRGB from CIE Lab (D50), through XYZ and a Bradford adaptation. */\nconst fromLab = (lightness: number, a: number, b: number) => {\n const epsilon = 216 / 24_389;\n const kappa = 24_389 / 27;\n const fy = (lightness + 16) / 116;\n const fx = fy + a / 500;\n const fz = fy - b / 200;\n const cube = (f: number): number =>\n f ** 3 > epsilon ? f ** 3 : (116 * f - 16) / kappa;\n const x = cube(fx) * 0.96422;\n const y = lightness > kappa * epsilon ? fy ** 3 : lightness / kappa;\n const z = cube(fz) * 0.82521;\n // D50 to D65.\n const x65 =\n 0.955473421488075 * x - 0.02309845494876471 * y + 0.06325924320057072 * z;\n const y65 =\n -0.0283697093338637 * x + 1.0099953980813041 * y + 0.021041441191917323 * z;\n const z65 =\n 0.012314014864481998 * x - 0.020507649298898964 * y + 1.330365926242124 * z;\n return [\n 3.2409699419045226 * x65 -\n 1.537383177570094 * y65 -\n 0.4986107602930034 * z65,\n -0.9692436362808796 * x65 +\n 1.8759675015077202 * y65 +\n 0.04155505740717559 * z65,\n 0.05563007969699366 * x65 -\n 0.20397695888897652 * y65 +\n 1.0569715142428786 * z65,\n ] as const;\n};\n\n/** Polar (lightness, chroma, hue) to rectangular (lightness, a, b). */\nconst rectangular = (lightness: number, chroma: number, hue: number) => {\n const radians = (hue * Math.PI) / 180;\n return [\n lightness,\n chroma * Math.cos(radians),\n chroma * Math.sin(radians),\n ] as const;\n};\n\n/** A color function's channels, converted to sRGB with `alpha`. */\ntype Converter = (channels: Channels, alpha: number) => Rgba;\n\nconst rgb: Converter = ([red, green, blue], alpha) => {\n const channel = (component: Component): number =>\n clamp(scaled(component, 255) / 255);\n return {\n alpha,\n blue: channel(blue),\n green: channel(green),\n red: channel(red),\n };\n};\n\n/** A converter for a hue-and-two-percentages function (`hsl()`, `hwb()`). */\nconst cylindrical =\n (\n convert: (hue: number, x: number, y: number) => readonly number[]\n ): Converter =>\n ([hue, x, y], alpha) => {\n const [red = 0, green = 0, blue = 0] = convert(\n hueDegrees(hue),\n clamp(scaled(x, 100) / 100),\n clamp(scaled(y, 100) / 100)\n );\n return { alpha, blue: clamp(blue), green: clamp(green), red: clamp(red) };\n };\n\nconst hsl = cylindrical(fromHsl);\n\n/** The color functions this module reads, by name. */\nconst CONVERTERS = new Map<string, Converter>([\n [\"hsl\", hsl],\n [\"hsla\", hsl],\n [\"hwb\", cylindrical(fromHwb)],\n [\n \"lab\",\n ([lightness, a, b], alpha) =>\n fromLinear(\n fromLab(scaled(lightness, 100), scaled(a, 125), scaled(b, 125)),\n alpha\n ),\n ],\n [\n \"lch\",\n ([lightness, chroma, hue], alpha) =>\n fromLinear(\n fromLab(\n ...rectangular(\n scaled(lightness, 100),\n scaled(chroma, 150),\n hueDegrees(hue)\n )\n ),\n alpha\n ),\n ],\n [\n \"oklab\",\n ([lightness, a, b], alpha) =>\n fromLinear(\n fromOklab(scaled(lightness, 1), scaled(a, 0.4), scaled(b, 0.4)),\n alpha\n ),\n ],\n [\n \"oklch\",\n ([lightness, chroma, hue], alpha) =>\n fromLinear(\n fromOklab(\n ...rectangular(\n scaled(lightness, 1),\n scaled(chroma, 0.4),\n hueDegrees(hue)\n )\n ),\n alpha\n ),\n ],\n [\"rgb\", rgb],\n [\"rgba\", rgb],\n]);\n\n/** Parse a CSS color to sRGB, or `null` for syntax this module doesn't read. */\nexport const parseColor = (value: string): Rgba | null => {\n const color = value.trim().toLowerCase();\n const hex = NAMED.get(color) ?? HEX.exec(color)?.groups?.digits;\n if (hex !== undefined) {\n return fromHex(hex);\n }\n const parsed = parseFunction(color);\n const convert = parsed && CONVERTERS.get(parsed.name);\n return parsed && convert\n ? convert(\n parsed.channels,\n parsed.alpha === undefined ? 1 : clamp(scaled(parsed.alpha, 1))\n )\n : null;\n};\n\n/** `color` painted over an opaque `backdrop`. */\nexport const composite = (color: Rgba, backdrop: Rgba): Rgba => {\n const mix = (top: number, bottom: number): number =>\n top * color.alpha + bottom * (1 - color.alpha);\n return {\n alpha: 1,\n blue: mix(color.blue, backdrop.blue),\n green: mix(color.green, backdrop.green),\n red: mix(color.red, backdrop.red),\n };\n};\n\n/** WCAG 2 relative luminance of an opaque color. */\nexport const luminance = (color: Rgba): number =>\n 0.2126 * decode(color.red) +\n 0.7152 * decode(color.green) +\n 0.0722 * decode(color.blue);\n\n/**\n * WCAG 2 contrast ratio of `foreground` on an opaque `background`, from 1 to\n * 21. A translucent foreground is painted over the background first.\n */\nexport const contrastRatio = (foreground: Rgba, background: Rgba): number => {\n const top = luminance(composite(foreground, background));\n const bottom = luminance(background);\n return (Math.max(top, bottom) + 0.05) / (Math.min(top, bottom) + 0.05);\n};\n",
|
|
6
|
+
"import type { ResolvedConfig } from \"../core/schema.ts\";\nimport { composite, contrastRatio, parseColor } from \"./color.ts\";\nimport type { Rgba } from \"./color.ts\";\n\n/** Per-mode CSS colors. */\nexport interface AccentColors {\n dark: string;\n light: string;\n}\n\n/** A color mode. */\nexport type ColorMode = keyof AccentColors;\n\n/**\n * Named accent presets mapped to OKLCH values, per mode. The single source of\n * truth for preset colors: the theme CSS and the OG card (og/card.ts) both\n * resolve from this table, so a site and its social cards can't disagree\n * about \"blue\". Each clears WCAG AA (4.5:1) as text on its mode's page and\n * under its button label ({@link readableOn}), with room to spare on the\n * tinted surfaces accent text also sits on: the light shades are dark enough\n * for white labels, the dark shades light enough to read on the dark page.\n */\nexport const ACCENTS = {\n blue: { dark: \"oklch(0.62 0.16 250)\", light: \"oklch(0.55 0.16 250)\" },\n green: { dark: \"oklch(0.6 0.16 150)\", light: \"oklch(0.53 0.16 150)\" },\n orange: { dark: \"oklch(0.68 0.17 50)\", light: \"oklch(0.56 0.17 50)\" },\n pink: { dark: \"oklch(0.65 0.2 350)\", light: \"oklch(0.58 0.2 350)\" },\n purple: { dark: \"oklch(0.6 0.2 290)\", light: \"oklch(0.57 0.2 290)\" },\n red: { dark: \"oklch(0.61 0.22 25)\", light: \"oklch(0.58 0.22 25)\" },\n teal: { dark: \"oklch(0.6 0.12 195)\", light: \"oklch(0.53 0.12 195)\" },\n} satisfies Record<string, AccentColors>;\n\n/** The contrast WCAG AA asks of body-size text. */\nexport const MIN_TEXT_CONTRAST = 4.5;\n\n/**\n * The theme's own colors a configured accent is drawn against: each mode's\n * page background and text, and the dark ink a filled accent can carry.\n * Mirrors the defaults in `entry.ts`.\n */\nexport const THEME_INKS = {\n dark: {\n background: \"oklch(0.085 0 0)\",\n foreground: \"oklch(0.96 0 0)\",\n ink: \"oklch(0.085 0 0)\",\n mutedForeground: \"oklch(0.68 0 0)\",\n },\n light: {\n background: \"oklch(1 0 0)\",\n foreground: \"oklch(0.145 0 0)\",\n ink: \"oklch(0.145 0 0)\",\n mutedForeground: \"oklch(0.53 0 0)\",\n },\n} satisfies Record<ColorMode, Record<string, string>>;\n\nconst WHITE = \"oklch(1 0 0)\";\n\n/** A color this module writes itself, parsed (they all parse). */\nconst own = (value: string): Rgba =>\n parseColor(value) ?? { alpha: 1, blue: 0, green: 0, red: 0 };\n\n/** {@link THEME_INKS}, parsed, for contrast math. */\nexport const themeInk = (\n mode: ColorMode,\n name: keyof (typeof THEME_INKS)[ColorMode]\n): Rgba => own(THEME_INKS[mode][name]);\n\n/**\n * The label color for text on a filled `fill` (a button, a step number): white\n * while it reaches AA, else the mode's dark ink when that reads better, so a\n * light accent (every dark-mode preset) gets dark text instead of white text\n * nobody can read.\n */\nexport const labelOn = (fill: Rgba, mode: ColorMode): string => {\n const opaque = composite(fill, themeInk(mode, \"background\"));\n const onWhite = contrastRatio(own(WHITE), opaque);\n return onWhite >= MIN_TEXT_CONTRAST ||\n onWhite >= contrastRatio(themeInk(mode, \"ink\"), opaque)\n ? WHITE\n : THEME_INKS[mode].ink;\n};\n\n/** {@link labelOn} for a CSS color; one this module can't parse keeps white. */\nexport const readableOn = (fill: string, mode: ColorMode): string => {\n const color = parseColor(fill);\n return color ? labelOn(color, mode) : WHITE;\n};\n\n/**\n * Whether a raw config value names an accent preset. `hasOwn` keeps a value\n * like \"constructor\" from resolving an Object.prototype member — which would\n * stringify a function into the generated CSS, breaking the rule (the exact\n * breakout {@link safeColor} exists to prevent).\n */\nexport const isAccentPreset = (value: string): value is keyof typeof ACCENTS =>\n Object.hasOwn(ACCENTS, value);\n\n// Characters valid in a CSS color value (hex, rgb/hsl/oklch functions, named\n// colors). Anything else — notably `;`, `{`, `}` — could break out of the\n// declaration and inject rules, so such a value is rejected.\nconst CSS_COLOR = /^[\\w\\s#%.,()/+-]+$/u;\n\n/** Pass a raw color through only if it can't break out of a CSS declaration. */\nconst safeColor = (value: string, fallback: string): string =>\n CSS_COLOR.test(value.trim()) ? value.trim() : fallback;\n\n/** Resolve a named preset in `mode`, or fall back to {@link safeColor}. */\nconst presetOrColor = (value: string, mode: ColorMode): string =>\n isAccentPreset(value)\n ? ACCENTS[value][mode]\n : safeColor(value, ACCENTS.blue[mode]);\n\n/** Like {@link safeColor} but drops an unsafe/absent value to `null`. */\nconst safeColorOrNull = (value: string | undefined): string | null =>\n value && CSS_COLOR.test(value.trim()) ? value.trim() : null;\n\nconst RADII = {\n lg: \"0.75rem\",\n md: \"0.5rem\",\n none: \"0\",\n sm: \"0.25rem\",\n} satisfies Record<ResolvedConfig[\"theme\"][\"radius\"], string>;\n\nconst cssString = (value: string): string => JSON.stringify(value);\n\nconst backgroundImageCss = (image: string): string =>\n `url(${cssString(image)})`;\n\nconst cssToken = (name: string, value?: string | null): string[] =>\n value ? [` ${name}: ${value};`] : [];\n\n/** One mode's resolved accent and action fills. */\ninterface ModeFills {\n accent: string;\n action: string | null;\n}\n\nconst themeRootCss = (\n theme: ResolvedConfig[\"theme\"],\n options: ModeFills & { radius: string }\n): string =>\n [\n ` --blume-accent: ${options.accent};`,\n ` --blume-accent-foreground: ${readableOn(options.accent, \"light\")};`,\n ...cssToken(\"--blume-action\", options.action),\n ...cssToken(\n \"--blume-action-foreground\",\n options.action ? readableOn(options.action, \"light\") : null\n ),\n ...cssToken(\"--blume-background\", safeColorOrNull(theme.background?.light)),\n ...cssToken(\n \"--blume-background-image\",\n theme.backgroundImage?.light\n ? backgroundImageCss(theme.backgroundImage.light)\n : null\n ),\n ` --blume-radius: ${options.radius};`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\nconst themeDarkCss = (\n theme: ResolvedConfig[\"theme\"],\n options: ModeFills\n): string => {\n // Mode-shared tokens (accent, action) must be re-declared here: the base\n // stylesheet's own `:root[data-theme=\"dark\"]` block outranks the `:root`\n // config tokens on specificity, so without this block dark mode would\n // silently keep its neutral defaults and ignore the config.\n const tokens = [\n ` --blume-accent: ${options.accent};`,\n ` --blume-accent-foreground: ${readableOn(options.accent, \"dark\")};`,\n ...cssToken(\"--blume-action\", options.action),\n ...cssToken(\n \"--blume-action-foreground\",\n options.action ? readableOn(options.action, \"dark\") : null\n ),\n ...cssToken(\"--blume-background\", safeColorOrNull(theme.background?.dark)),\n ...cssToken(\n \"--blume-background-image\",\n theme.backgroundImage?.dark\n ? backgroundImageCss(theme.backgroundImage.dark)\n : null\n ),\n ].filter(Boolean);\n return `:root[data-theme=\"dark\"] {\n${tokens.join(\"\\n\")}\n}\n`;\n};\n\n/**\n * Resolve the configured accent to per-mode CSS colors. A named accent\n * resolves to its preset's shade for each mode; any other value is treated as\n * a raw CSS color so users can pass arbitrary colors without a config change.\n * A string accent has already been normalized by the config schema to the\n * same value for both modes.\n */\nexport const resolveAccent = (\n theme: ResolvedConfig[\"theme\"]\n): AccentColors => ({\n dark: presetOrColor(theme.accent.dark, \"dark\"),\n light: presetOrColor(theme.accent.light, \"light\"),\n});\n\n/** Resolve the configured `action` fill per mode, or `null` when unset. */\nexport const resolveAction = (\n theme: ResolvedConfig[\"theme\"]\n): AccentColors | null =>\n theme.action\n ? {\n dark: presetOrColor(theme.action, \"dark\"),\n light: presetOrColor(theme.action, \"light\"),\n }\n : null;\n\n/** Resolve the configured radius preset to a CSS length. */\nexport const resolveRadius = (theme: ResolvedConfig[\"theme\"]): string =>\n RADII[theme.radius];\n\n/**\n * Compile theme config into CSS custom properties. A named accent resolves to\n * its preset; any other value is treated as a raw CSS color so users can pass\n * arbitrary colors without a config change. Labels on accent and action fills\n * take whichever of white or dark ink reads ({@link readableOn}).\n */\nexport const buildThemeCss = (theme: ResolvedConfig[\"theme\"]): string => {\n const accent = resolveAccent(theme);\n const action = resolveAction(theme);\n const root = themeRootCss(theme, {\n accent: accent.light,\n action: action?.light ?? null,\n radius: RADII[theme.radius],\n });\n const dark = themeDarkCss(theme, {\n accent: accent.dark,\n action: action?.dark ?? null,\n });\n\n return `/* Generated by Blume from theme config. */\n:root {\n${root}\n}\n${dark}`;\n};\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "9A390AB579BDCE3E64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveRuntimeDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-xkz5xbtv.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./index.js";
|
|
@@ -9,13 +9,58 @@ import {
|
|
|
9
9
|
// src/cli/dev-lock.ts
|
|
10
10
|
import {
|
|
11
11
|
existsSync,
|
|
12
|
+
linkSync,
|
|
12
13
|
mkdirSync,
|
|
13
14
|
readFileSync,
|
|
15
|
+
renameSync,
|
|
14
16
|
rmSync,
|
|
17
|
+
statSync,
|
|
15
18
|
writeFileSync
|
|
16
19
|
} from "node:fs";
|
|
17
20
|
import { join } from "pathe";
|
|
18
21
|
var lockPath = (outDir) => join(outDir, "dev.lock");
|
|
22
|
+
var reapPath = (outDir) => join(outDir, "dev.lock.reap");
|
|
23
|
+
var PARTIAL_LOCK_GRACE_MS = 2000;
|
|
24
|
+
var RETRY_DELAY_MS = 5;
|
|
25
|
+
var pause = (ms) => {
|
|
26
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
27
|
+
};
|
|
28
|
+
var tempSequence = 0;
|
|
29
|
+
var tempPath = (path) => {
|
|
30
|
+
tempSequence += 1;
|
|
31
|
+
return `${path}.${process.pid}.${tempSequence}.tmp`;
|
|
32
|
+
};
|
|
33
|
+
var createExclusive = (path, payload) => {
|
|
34
|
+
const temp = tempPath(path);
|
|
35
|
+
writeFileSync(temp, payload);
|
|
36
|
+
try {
|
|
37
|
+
linkSync(temp, path);
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error.code !== "EEXIST") {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
} finally {
|
|
45
|
+
rmSync(temp, { force: true });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var replaceFile = (path, payload) => {
|
|
49
|
+
const temp = tempPath(path);
|
|
50
|
+
writeFileSync(temp, payload);
|
|
51
|
+
renameSync(temp, path);
|
|
52
|
+
};
|
|
53
|
+
var readIfExists = (path) => {
|
|
54
|
+
try {
|
|
55
|
+
const raw = readFileSync(path, "utf-8");
|
|
56
|
+
return { mtimeMs: statSync(path).mtimeMs, raw };
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error.code !== "ENOENT") {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
19
64
|
var isValidPid = (pid) => typeof pid === "number" && Number.isInteger(pid) && pid > 0;
|
|
20
65
|
var isPortNumber = (port) => typeof port === "number";
|
|
21
66
|
var isLockRecord = (data) => typeof data === "object" && data !== null;
|
|
@@ -45,13 +90,21 @@ var isProcessAlive = (pid) => {
|
|
|
45
90
|
return error.code === "EPERM";
|
|
46
91
|
}
|
|
47
92
|
};
|
|
48
|
-
var
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
51
|
-
return
|
|
93
|
+
var inspectLock = (outDir) => {
|
|
94
|
+
const file = readIfExists(lockPath(outDir));
|
|
95
|
+
if (!file) {
|
|
96
|
+
return { kind: "absent" };
|
|
97
|
+
}
|
|
98
|
+
const lock = parseLock(file.raw);
|
|
99
|
+
if (!lock) {
|
|
100
|
+
return Date.now() - file.mtimeMs < PARTIAL_LOCK_GRACE_MS ? { kind: "busy" } : { kind: "stale" };
|
|
52
101
|
}
|
|
53
|
-
|
|
54
|
-
|
|
102
|
+
return isProcessAlive(lock.pid) ? { kind: "live", lock } : { kind: "stale" };
|
|
103
|
+
};
|
|
104
|
+
var isClaimable = (state) => state.kind === "stale" || state.kind === "live" && state.lock.pid === process.pid;
|
|
105
|
+
var readDevLock = (outDir) => {
|
|
106
|
+
const state = inspectLock(outDir);
|
|
107
|
+
return state.kind === "live" ? state.lock : null;
|
|
55
108
|
};
|
|
56
109
|
var lockPayload = (port) => {
|
|
57
110
|
const info = { pid: process.pid };
|
|
@@ -60,9 +113,6 @@ var lockPayload = (port) => {
|
|
|
60
113
|
}
|
|
61
114
|
return JSON.stringify(info);
|
|
62
115
|
};
|
|
63
|
-
var writeLock = (outDir, port) => {
|
|
64
|
-
writeFileSync(lockPath(outDir), lockPayload(port));
|
|
65
|
-
};
|
|
66
116
|
|
|
67
117
|
class DevLockHeldError extends Error {
|
|
68
118
|
lock;
|
|
@@ -83,25 +133,39 @@ var ownsLock = (outDir) => {
|
|
|
83
133
|
return false;
|
|
84
134
|
}
|
|
85
135
|
};
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
136
|
+
var reapStaleLock = (outDir) => {
|
|
137
|
+
const mutex = reapPath(outDir);
|
|
138
|
+
if (!createExclusive(mutex, lockPayload())) {
|
|
139
|
+
const holder = readIfExists(mutex);
|
|
140
|
+
const pid = holder ? parseLock(holder.raw)?.pid : undefined;
|
|
141
|
+
if (pid !== undefined && pid !== process.pid && isProcessAlive(pid)) {
|
|
142
|
+
pause(RETRY_DELAY_MS);
|
|
143
|
+
} else if (holder) {
|
|
144
|
+
rmSync(mutex, { force: true });
|
|
93
145
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
if (isClaimable(inspectLock(outDir))) {
|
|
150
|
+
rmSync(lockPath(outDir), { force: true });
|
|
97
151
|
}
|
|
98
|
-
|
|
99
|
-
|
|
152
|
+
} finally {
|
|
153
|
+
rmSync(mutex, { force: true });
|
|
100
154
|
}
|
|
101
155
|
};
|
|
102
156
|
var acquireDevLock = (outDir, port) => {
|
|
103
157
|
mkdirSync(outDir, { recursive: true });
|
|
104
|
-
|
|
158
|
+
const payload = lockPayload(port);
|
|
159
|
+
while (!createExclusive(lockPath(outDir), payload)) {
|
|
160
|
+
const state = inspectLock(outDir);
|
|
161
|
+
if (isClaimable(state)) {
|
|
162
|
+
reapStaleLock(outDir);
|
|
163
|
+
} else if (state.kind === "live") {
|
|
164
|
+
throw new DevLockHeldError(state.lock);
|
|
165
|
+
} else if (state.kind === "busy") {
|
|
166
|
+
pause(RETRY_DELAY_MS);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
105
169
|
let released = false;
|
|
106
170
|
return () => {
|
|
107
171
|
if (released) {
|
|
@@ -117,7 +181,7 @@ var acquireDevLock = (outDir, port) => {
|
|
|
117
181
|
};
|
|
118
182
|
var updateDevLockPort = (outDir, port) => {
|
|
119
183
|
if (ownsLock(outDir)) {
|
|
120
|
-
|
|
184
|
+
replaceFile(lockPath(outDir), lockPayload(port));
|
|
121
185
|
}
|
|
122
186
|
};
|
|
123
187
|
var describeDevLock = (lock) => lock.port === undefined ? "" : ` at http://localhost:${lock.port}`;
|
|
@@ -132,5 +196,5 @@ var refuseIfDevRunning = (root, action, options = {}) => {
|
|
|
132
196
|
|
|
133
197
|
export { readDevLock, DevLockHeldError, acquireDevLock, updateDevLockPort, describeDevLock, refuseIfDevRunning };
|
|
134
198
|
|
|
135
|
-
//# debugId=
|
|
136
|
-
//# sourceMappingURL=chunk-
|
|
199
|
+
//# debugId=BA0F0BBCC7BD124F64756E2164756E21
|
|
200
|
+
//# sourceMappingURL=chunk-cmtcnh9f.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/dev-lock.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n existsSync,\n linkSync,\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\n\nimport { join } from \"pathe\";\n\nimport { resolveRuntimeDir } from \"../core/project.ts\";\nimport { logger } from \"./log.ts\";\n\n/**\n * A best-effort PID lock in the shared `.blume/` runtime dir. `blume dev`\n * regenerates and serves `.blume` continuously, so a concurrent `build`,\n * `eject`, or second `dev` that regenerates or deletes it out from under the\n * running Vite server corrupts the dev session. The lock lets those commands\n * detect a live dev server and refuse — and, because it records the server's\n * port, point the caller (often an agent that just tried to start its own\n * server) at the URL to reuse instead.\n */\n\nexport interface DevLockInfo {\n pid: number;\n /** Port the dev server is bound to, when known. */\n port?: number;\n}\n\nconst lockPath = (outDir: string): string => join(outDir, \"dev.lock\");\n\n/** The mutex a process holds while it clears a stale lock. */\nconst reapPath = (outDir: string): string => join(outDir, \"dev.lock.reap\");\n\n/**\n * How long a lock file that doesn't parse counts as one being written right\n * now. Blume creates a lock with its whole content in place (see\n * {@link createExclusive}), but one left empty or cut off — by a crash, or by\n * an older Blume that opened the file before writing it — would otherwise\n * read as stale the instant it appears. Past this age it's debris.\n */\nconst PARTIAL_LOCK_GRACE_MS = 2000;\n\n/** Pause between claim attempts while another process is mid-claim. */\nconst RETRY_DELAY_MS = 5;\n\nconst pause = (ms: number): void => {\n // A synchronous sleep: the claim is synchronous, and what it waits on is\n // another process, so there's nothing in this one to yield to.\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n};\n\n/** Per-process counter that keeps concurrent temp file names apart. */\nlet tempSequence = 0;\n\n/** A private sibling of `path` to stage a write in. */\nconst tempPath = (path: string): string => {\n tempSequence += 1;\n return `${path}.${process.pid}.${tempSequence}.tmp`;\n};\n\n/**\n * Create `path` holding `payload`, atomically and exclusively. The payload\n * goes to a private temp file first, which is then hard-linked into place:\n * the link fails when `path` exists, exactly like a `wx` open, but the file\n * appears with its whole content — no reader ever sees it empty or half\n * written. Returns false when `path` already exists.\n */\nconst createExclusive = (path: string, payload: string): boolean => {\n const temp = tempPath(path);\n writeFileSync(temp, payload);\n try {\n linkSync(temp, path);\n return true;\n } catch (error) {\n // SAFETY: `linkSync` failures are errno exceptions.\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") {\n throw error;\n }\n return false;\n } finally {\n rmSync(temp, { force: true });\n }\n};\n\n/** Replace `path` with `payload` in one step, so readers see old or new. */\nconst replaceFile = (path: string, payload: string): void => {\n const temp = tempPath(path);\n writeFileSync(temp, payload);\n renameSync(temp, path);\n};\n\n/** A file's text, or null when it doesn't exist (anymore). */\nconst readIfExists = (\n path: string\n): { raw: string; mtimeMs: number } | null => {\n try {\n const raw = readFileSync(path, \"utf-8\");\n return { mtimeMs: statSync(path).mtimeMs, raw };\n } catch (error) {\n // SAFETY: `readFileSync`/`statSync` failures are errno exceptions.\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw error;\n }\n return null;\n }\n};\n\n/** What `JSON.parse` can yield for a lock file body. */\ntype LockFileValue =\n | string\n | number\n | boolean\n | null\n | LockFileValue[]\n | { [key: string]: LockFileValue };\n\nconst isValidPid = (pid: LockFileValue | undefined): pid is number =>\n typeof pid === \"number\" && Number.isInteger(pid) && pid > 0;\n\nconst isPortNumber = (port: LockFileValue | undefined): port is number =>\n typeof port === \"number\";\n\nconst isLockRecord = (\n data: LockFileValue\n): data is { pid?: LockFileValue; port?: LockFileValue } =>\n typeof data === \"object\" && data !== null;\n\n/**\n * Parse a lock file body. Current locks are JSON (`{\"pid\":123,\"port\":3001}`);\n * a bare integer (the pre-port format) still parses as a pid-only lock.\n */\nconst parseLock = (raw: string): DevLockInfo | null => {\n let data: LockFileValue;\n try {\n data = JSON.parse(raw.trim());\n } catch {\n return null;\n }\n if (isValidPid(data)) {\n return { pid: data };\n }\n if (isLockRecord(data)) {\n const { pid, port } = data;\n if (isValidPid(pid)) {\n return isPortNumber(port) ? { pid, port } : { pid };\n }\n }\n return null;\n};\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n // Signal 0 probes liveness without actually signaling the process.\n process.kill(pid, 0);\n return true;\n } catch (error) {\n // EPERM means the process exists but belongs to another user — still\n // live, so the lock must hold (only ESRCH proves it's gone).\n // SAFETY: `process.kill` failures are errno exceptions.\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n};\n\n/** What the lock file on a dir says, read once. */\ntype LockState =\n | { kind: \"absent\" }\n /** Unparseable but brand new: another process is writing it right now. */\n | { kind: \"busy\" }\n | { kind: \"live\"; lock: DevLockInfo }\n /** A dead holder's lock, or unparseable debris. */\n | { kind: \"stale\" };\n\nconst inspectLock = (outDir: string): LockState => {\n const file = readIfExists(lockPath(outDir));\n if (!file) {\n return { kind: \"absent\" };\n }\n const lock = parseLock(file.raw);\n if (!lock) {\n return Date.now() - file.mtimeMs < PARTIAL_LOCK_GRACE_MS\n ? { kind: \"busy\" }\n : { kind: \"stale\" };\n }\n return isProcessAlive(lock.pid) ? { kind: \"live\", lock } : { kind: \"stale\" };\n};\n\n/**\n * Whether this process may clear the lock to claim the dir: a dead holder's\n * lock and debris, or this process's own leftover.\n */\nconst isClaimable = (state: LockState): boolean =>\n state.kind === \"stale\" ||\n (state.kind === \"live\" && state.lock.pid === process.pid);\n\n/**\n * Read the lock on `outDir` held by a live `blume dev`, or null. A lock left\n * by a process that has since exited (stale) is treated as absent.\n */\nexport const readDevLock = (outDir: string): DevLockInfo | null => {\n const state = inspectLock(outDir);\n return state.kind === \"live\" ? state.lock : null;\n};\n\n/** Whether another live `blume dev` holds the lock on `outDir`. */\nexport const isDevLocked = (outDir: string): boolean =>\n readDevLock(outDir) !== null;\n\nconst lockPayload = (port?: number): string => {\n const info: DevLockInfo = { pid: process.pid };\n if (port !== undefined) {\n info.port = port;\n }\n return JSON.stringify(info);\n};\n\n/** Thrown when another live `blume dev` already holds the lock. */\nexport class DevLockHeldError extends Error {\n readonly lock: DevLockInfo;\n\n constructor(lock: DevLockInfo) {\n super(`A blume dev server (pid ${lock.pid}) already holds the lock.`);\n this.name = \"DevLockHeldError\";\n this.lock = lock;\n }\n}\n\nconst ownsLock = (outDir: string): boolean => {\n const path = lockPath(outDir);\n if (!existsSync(path)) {\n return false;\n }\n try {\n return parseLock(readFileSync(path, \"utf-8\"))?.pid === process.pid;\n } catch {\n return false;\n }\n};\n\n/**\n * Clear a claimable lock without racing anyone else doing the same. Two\n * processes can both read the same stale lock; if each simply deleted the\n * file, the slower one could delete the lock the faster one had claimed in\n * between, and both would win. So removing a lock takes the reap mutex\n * (created the same atomic way), and whoever holds it reads the lock again\n * first: a live lock claimed since is left alone. A mutex whose holder died\n * mid-reap is cleared for the next attempt.\n */\nconst reapStaleLock = (outDir: string): void => {\n const mutex = reapPath(outDir);\n if (!createExclusive(mutex, lockPayload())) {\n const holder = readIfExists(mutex);\n const pid = holder ? parseLock(holder.raw)?.pid : undefined;\n if (pid !== undefined && pid !== process.pid && isProcessAlive(pid)) {\n pause(RETRY_DELAY_MS);\n } else if (holder) {\n rmSync(mutex, { force: true });\n }\n return;\n }\n try {\n if (isClaimable(inspectLock(outDir))) {\n rmSync(lockPath(outDir), { force: true });\n }\n } finally {\n rmSync(mutex, { force: true });\n }\n};\n\n/**\n * Write the current process's dev lock into `outDir` and return a release\n * function. Two `blume dev` processes starting at the same moment can't both\n * win: the lock appears whole and exclusively (a hard link, which fails when\n * the file exists), a lock still being written is waited on rather than read\n * as stale, and a stale lock (dead pid) or this process's own leftover is only\n * cleared under the reap mutex — the loser gets a {@link DevLockHeldError}\n * naming the live holder. The release only removes the file if it's still\n * ours, so a newer dev server's lock is never clobbered.\n */\nexport const acquireDevLock = (outDir: string, port?: number): (() => void) => {\n mkdirSync(outDir, { recursive: true });\n const payload = lockPayload(port);\n while (!createExclusive(lockPath(outDir), payload)) {\n const state = inspectLock(outDir);\n if (isClaimable(state)) {\n reapStaleLock(outDir);\n } else if (state.kind === \"live\") {\n throw new DevLockHeldError(state.lock);\n } else if (state.kind === \"busy\") {\n pause(RETRY_DELAY_MS);\n }\n // \"absent\": the holder released it just now — race for the claim again.\n }\n let released = false;\n return () => {\n if (released) {\n return;\n }\n released = true;\n try {\n if (ownsLock(outDir)) {\n rmSync(lockPath(outDir), { force: true });\n }\n } catch {\n // Best-effort cleanup; a stale lock is handled by the liveness check.\n }\n };\n};\n\n/**\n * Rewrite this process's lock with the port the server actually bound (the\n * lock is acquired before the server starts, and Vite may bump a busy port).\n * A lock owned by another process is left alone.\n */\nexport const updateDevLockPort = (outDir: string, port: number): void => {\n if (ownsLock(outDir)) {\n // Replaced in one step: rewriting in place would leave a moment where a\n // concurrent claim reads an empty lock.\n replaceFile(lockPath(outDir), lockPayload(port));\n }\n};\n\n/** Human-readable location of a locked dev server, e.g. \" at http://localhost:3001\". */\nexport const describeDevLock = (lock: DevLockInfo): string =>\n lock.port === undefined ? \"\" : ` at http://localhost:${lock.port}`;\n\n/**\n * Exit with an error when a live `blume dev` owns the runtime dir under `root`.\n * `action` names the operation being refused (e.g. \"building\"). `runtimeDir`\n * relocates the checked dir: an isolated verify (`.blume-verify`) targets a dir\n * dev never locks, so it proceeds; a default or `--runtime-dir .blume` run still\n * refuses. Only commands that actually accept `--isolated` (build, check) should\n * set `isolatedHint`, so the refusal never suggests a flag the command ignores.\n */\nexport const refuseIfDevRunning = (\n root: string,\n action: string,\n options: { runtimeDir?: string; isolatedHint?: boolean } = {}\n): void => {\n const lock = readDevLock(resolveRuntimeDir(root, options.runtimeDir));\n if (lock) {\n const remedies = options.isolatedHint\n ? \"Reuse that server, stop it first, or re-run with --isolated to build/verify against .blume-verify without touching it.\"\n : \"Reuse that server or stop it first.\";\n logger.error(\n `A \\`blume dev\\` server is running${describeDevLock(lock)}; ${action} would corrupt its .blume runtime. ${remedies}`\n );\n process.exit(1);\n }\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "BA0F0BBCC7BD124F64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
needsPlaygroundProxy,
|
|
4
|
+
resolveAskBackend
|
|
5
|
+
} from "./chunk-xkz5xbtv.js";
|
|
6
|
+
|
|
7
|
+
// src/core/server-features.ts
|
|
8
|
+
var serverFeatures = (config) => {
|
|
9
|
+
const features = [];
|
|
10
|
+
if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
|
|
11
|
+
features.push("Assistant");
|
|
12
|
+
}
|
|
13
|
+
if (config.agents.mcp.enabled) {
|
|
14
|
+
features.push("MCP server");
|
|
15
|
+
}
|
|
16
|
+
if (needsPlaygroundProxy(config)) {
|
|
17
|
+
features.push("API playground proxy");
|
|
18
|
+
}
|
|
19
|
+
if (config.search.provider.mode === "server") {
|
|
20
|
+
features.push(`Search (${config.search.provider.kind})`);
|
|
21
|
+
}
|
|
22
|
+
return features;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/cli/required-secrets.ts
|
|
26
|
+
var adapterSecretOwners = (config) => {
|
|
27
|
+
const { consent, deployment, rateLimit } = config;
|
|
28
|
+
const { provider } = config.search;
|
|
29
|
+
const owners = [
|
|
30
|
+
{ feature: `Search (${provider.kind})`, secrets: provider.requiredSecrets },
|
|
31
|
+
...config.analytics.map((adapter) => ({
|
|
32
|
+
feature: `Analytics (${adapter.kind})`,
|
|
33
|
+
secrets: adapter.requiredSecrets
|
|
34
|
+
})),
|
|
35
|
+
...config.content.sources.map((source) => ({
|
|
36
|
+
feature: `Content source (${source.kind})`,
|
|
37
|
+
secrets: source.requiredSecrets
|
|
38
|
+
})),
|
|
39
|
+
...config.reference.map((adapter) => ({
|
|
40
|
+
feature: `API reference (${adapter.kind})`,
|
|
41
|
+
secrets: adapter.requiredSecrets
|
|
42
|
+
})),
|
|
43
|
+
{
|
|
44
|
+
feature: `Deployment (${deployment.kind})`,
|
|
45
|
+
secrets: deployment.requiredSecrets
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
if (consent) {
|
|
49
|
+
owners.push({
|
|
50
|
+
feature: `Consent (${consent.kind})`,
|
|
51
|
+
secrets: consent.requiredSecrets
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (rateLimit) {
|
|
55
|
+
owners.push({
|
|
56
|
+
feature: `Rate limiting (${rateLimit.kind})`,
|
|
57
|
+
note: "read at request time; without it the routes count in memory",
|
|
58
|
+
secrets: rateLimit.requiredSecrets
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return owners;
|
|
62
|
+
};
|
|
63
|
+
var checkRequiredSecrets = (config) => {
|
|
64
|
+
const diagnostics = [];
|
|
65
|
+
const requireSecret = (feature, env, note) => {
|
|
66
|
+
if (process.env[env]) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const noteSuffix = note ? ` (${note})` : "";
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
code: "BLUME_MISSING_SECRET",
|
|
72
|
+
message: `${feature} is enabled but ${env} is not set${noteSuffix}.`,
|
|
73
|
+
severity: "warning",
|
|
74
|
+
suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
|
|
78
|
+
const { captcha, provider } = config.ai.assistant;
|
|
79
|
+
const backend = resolveAskBackend(provider);
|
|
80
|
+
for (const env of provider.requiredSecrets) {
|
|
81
|
+
requireSecret(`Assistant (${backend.label})`, env, backend.secretNote);
|
|
82
|
+
}
|
|
83
|
+
for (const env of captcha?.requiredSecrets ?? []) {
|
|
84
|
+
requireSecret(`Assistant bot check (${captcha?.kind})`, env);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const { narration } = config;
|
|
88
|
+
if (narration.enabled && narration.provider) {
|
|
89
|
+
for (const env of narration.provider.requiredSecrets) {
|
|
90
|
+
requireSecret("Narration audio", env, "read at build; without it pages use browser voices");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const owner of adapterSecretOwners(config)) {
|
|
94
|
+
for (const env of owner.secrets) {
|
|
95
|
+
requireSecret(owner.feature, env, owner.note);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return diagnostics;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export { serverFeatures, checkRequiredSecrets };
|
|
102
|
+
|
|
103
|
+
//# debugId=D62D6223654FF46464756E2164756E21
|
|
104
|
+
//# sourceMappingURL=chunk-cy5bm0j0.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/server-features.ts", "../src/cli/required-secrets.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { needsPlaygroundProxy } from \"../openapi/references.ts\";\nimport type { ResolvedConfig } from \"./schema.ts\";\n\n/**\n * List the enabled features that require Astro server output. Static builds\n * fail clearly when any of these are enabled.\n */\nexport const serverFeatures = (config: ResolvedConfig): string[] => {\n const features: string[] = [];\n if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {\n features.push(\"Assistant\");\n }\n // The hosted MCP server is a live JSON-RPC endpoint, so it needs a runtime.\n if (config.agents.mcp.enabled) {\n features.push(\"MCP server\");\n }\n // The built-in playground proxy (`playground.proxy: true` on an `openapi()`\n // or `graphql()` adapter) is a live fetch endpoint at `/_api-proxy`; an\n // external proxy URL (string) or a proxy-less playground stays fully static.\n if (needsPlaygroundProxy(config)) {\n features.push(\"API playground proxy\");\n }\n // A `server` adapter (Mixedbread) proxies queries through a secret server\n // endpoint, so it can't run on a static build.\n if (config.search.provider.mode === \"server\") {\n features.push(`Search (${config.search.provider.kind})`);\n }\n return features;\n};\n",
|
|
6
|
+
"import { resolveAskBackend } from \"../ai/ask.ts\";\nimport type { ResolvedConfig } from \"../core/schema.ts\";\nimport type { Diagnostic } from \"../core/types.ts\";\n\n/** A feature, and the secrets the adapter behind it reads. */\ninterface SecretOwner {\n feature: string;\n note?: string;\n secrets: string[];\n}\n\n/**\n * The adapters that name their own secrets. The built-ins mostly declare\n * none: analytics ships public tokens in the page, consent managers load\n * public scripts, reference adapters read public specs, and host adapters\n * read platform-injected env.\n */\nconst adapterSecretOwners = (config: ResolvedConfig): SecretOwner[] => {\n const { consent, deployment, rateLimit } = config;\n const { provider } = config.search;\n const owners: SecretOwner[] = [\n // Each search adapter declares the secrets its generated runtime reads.\n { feature: `Search (${provider.kind})`, secrets: provider.requiredSecrets },\n ...config.analytics.map((adapter) => ({\n feature: `Analytics (${adapter.kind})`,\n secrets: adapter.requiredSecrets,\n })),\n // Each content source adapter declares the env vars its fetch reads.\n ...config.content.sources.map((source) => ({\n feature: `Content source (${source.kind})`,\n secrets: source.requiredSecrets,\n })),\n ...config.reference.map((adapter) => ({\n feature: `API reference (${adapter.kind})`,\n secrets: adapter.requiredSecrets,\n })),\n {\n feature: `Deployment (${deployment.kind})`,\n secrets: deployment.requiredSecrets,\n },\n ];\n if (consent) {\n owners.push({\n feature: `Consent (${consent.kind})`,\n secrets: consent.requiredSecrets,\n });\n }\n // Upstash's endpoint and token.\n if (rateLimit) {\n owners.push({\n feature: `Rate limiting (${rateLimit.kind})`,\n note: \"read at request time; without it the routes count in memory\",\n secrets: rateLimit.requiredSecrets,\n });\n }\n return owners;\n};\n\n/**\n * Warn early when an enabled feature needs a secret env var that isn't set, so\n * the failure surfaces at `blume dev`/`build` instead of at the first request in\n * production. These are runtime secrets (the endpoint reads them on the server),\n * so this warns rather than hard-fails — the value may live only in the deploy\n * environment. Build-time secrets (search-index sync) already warn during sync.\n */\nexport const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {\n const diagnostics: Diagnostic[] = [];\n const requireSecret = (feature: string, env: string, note?: string): void => {\n if (process.env[env]) {\n return;\n }\n const noteSuffix = note ? ` (${note})` : \"\";\n diagnostics.push({\n code: \"BLUME_MISSING_SECRET\",\n message: `${feature} is enabled but ${env} is not set${noteSuffix}.`,\n severity: \"warning\",\n suggestion: `Set ${env} in .env.local for local dev, or in your host's environment for production.`,\n });\n };\n\n if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {\n // The adapter descriptor names the env vars its route reads.\n const { captcha, provider } = config.ai.assistant;\n const backend = resolveAskBackend(provider);\n for (const env of provider.requiredSecrets) {\n requireSecret(`Assistant (${backend.label})`, env, backend.secretNote);\n }\n // The bot check's secret key, which the route verifies tokens with.\n for (const env of captcha?.requiredSecrets ?? []) {\n requireSecret(`Assistant bot check (${captcha?.kind})`, env);\n }\n }\n\n // Generated narration reads its key at build, not at runtime; without it the\n // build skips the audio and pages read with browser voices.\n const { narration } = config;\n if (narration.enabled && narration.provider) {\n for (const env of narration.provider.requiredSecrets) {\n requireSecret(\n \"Narration audio\",\n env,\n \"read at build; without it pages use browser voices\"\n );\n }\n }\n\n for (const owner of adapterSecretOwners(config)) {\n for (const env of owner.secrets) {\n requireSecret(owner.feature, env, owner.note);\n }\n }\n\n return diagnostics;\n};\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "D62D6223654FF46464756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -3,19 +3,20 @@ import {
|
|
|
3
3
|
writeTextAtomic
|
|
4
4
|
} from "./chunk-jq5n4avg.js";
|
|
5
5
|
import {
|
|
6
|
-
nextFenceState,
|
|
7
6
|
localizeRoute,
|
|
7
|
+
nextFenceState,
|
|
8
8
|
VERSION_ID,
|
|
9
9
|
VERSION_LIKE,
|
|
10
10
|
versionizeRoute
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-xkz5xbtv.js";
|
|
12
12
|
import {
|
|
13
|
+
mountBasePath,
|
|
13
14
|
stripBasePath
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-zccbjcj0.js";
|
|
15
16
|
import {
|
|
16
17
|
isWithin,
|
|
17
18
|
scanProject
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-43zcf4gz.js";
|
|
19
20
|
|
|
20
21
|
// src/core/version-cut.ts
|
|
21
22
|
import { existsSync } from "node:fs";
|
|
@@ -28,7 +29,7 @@ class CutError extends Error {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
var INLINE_CODE = /`[^`]*`/gu;
|
|
31
|
-
var ROOT_LINK = /(?<prefix>\]\(
|
|
32
|
+
var ROOT_LINK = /(?<prefix>\]\(|^ {0,3}\[(?!\^)[^\]]+\]:[ \t]*<?|(?:href|src)=["'])(?<target>\/[^\s"'<>)]*)/gu;
|
|
32
33
|
var buildRouteRewrites = (project, id) => {
|
|
33
34
|
const { basePath, i18n } = project.config;
|
|
34
35
|
const { contentRoot } = project.context;
|
|
@@ -52,7 +53,15 @@ var buildRouteRewrites = (project, id) => {
|
|
|
52
53
|
}
|
|
53
54
|
return rewrites;
|
|
54
55
|
};
|
|
55
|
-
var
|
|
56
|
+
var snapshotTarget = (path, rewrites, basePath) => {
|
|
57
|
+
const based = basePath !== "" && (path === basePath || path.startsWith(`${basePath}/`));
|
|
58
|
+
if (!based) {
|
|
59
|
+
return rewrites.get(path);
|
|
60
|
+
}
|
|
61
|
+
const replacement = rewrites.get(stripBasePath(basePath, path));
|
|
62
|
+
return replacement === undefined ? undefined : mountBasePath(basePath, replacement);
|
|
63
|
+
};
|
|
64
|
+
var rewriteLine = (line, rewrites, basePath) => {
|
|
56
65
|
const masked = line.replaceAll(INLINE_CODE, (span) => " ".repeat(span.length));
|
|
57
66
|
let out = "";
|
|
58
67
|
let cursor = 0;
|
|
@@ -63,7 +72,7 @@ var rewriteLine = (line, rewrites) => {
|
|
|
63
72
|
const path = hash === -1 ? target : target.slice(0, hash);
|
|
64
73
|
const suffix = hash === -1 ? "" : target.slice(hash);
|
|
65
74
|
const bare = path !== "/" && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
66
|
-
const replacement =
|
|
75
|
+
const replacement = snapshotTarget(bare, rewrites, basePath);
|
|
67
76
|
if (replacement === undefined) {
|
|
68
77
|
continue;
|
|
69
78
|
}
|
|
@@ -74,7 +83,7 @@ var rewriteLine = (line, rewrites) => {
|
|
|
74
83
|
}
|
|
75
84
|
return count === 0 ? line : out + line.slice(cursor);
|
|
76
85
|
};
|
|
77
|
-
var rewriteSnapshotLinks = (source, rewrites) => {
|
|
86
|
+
var rewriteSnapshotLinks = (source, rewrites, basePath = "") => {
|
|
78
87
|
let fence = null;
|
|
79
88
|
let count = 0;
|
|
80
89
|
const lines = source.split(`
|
|
@@ -85,7 +94,7 @@ var rewriteSnapshotLinks = (source, rewrites) => {
|
|
|
85
94
|
if (inFence) {
|
|
86
95
|
return line;
|
|
87
96
|
}
|
|
88
|
-
const rewrittenLine = rewriteLine(line, rewrites);
|
|
97
|
+
const rewrittenLine = rewriteLine(line, rewrites, basePath);
|
|
89
98
|
if (rewrittenLine !== line) {
|
|
90
99
|
count += 1;
|
|
91
100
|
}
|
|
@@ -247,7 +256,7 @@ var cutVersion = async (root, id, options = {}) => {
|
|
|
247
256
|
return;
|
|
248
257
|
}
|
|
249
258
|
const source = await readFile(abs, "utf-8");
|
|
250
|
-
const { text, count } = rewriteSnapshotLinks(source, rewrites);
|
|
259
|
+
const { text, count } = rewriteSnapshotLinks(source, rewrites, project.config.basePath);
|
|
251
260
|
if (count > 0) {
|
|
252
261
|
await writeTextAtomic(abs, text);
|
|
253
262
|
rewritten.push({ count, file: relative(dir, abs) });
|
|
@@ -270,5 +279,5 @@ var cutVersion = async (root, id, options = {}) => {
|
|
|
270
279
|
|
|
271
280
|
export { CutError, unregisteredSnapshotDiagnostics, cutVersion };
|
|
272
281
|
|
|
273
|
-
//# debugId=
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
282
|
+
//# debugId=8631919BA7BA49B664756E2164756E21
|
|
283
|
+
//# sourceMappingURL=chunk-e7vvk5ya.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/version-cut.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { existsSync } from \"node:fs\";\nimport { cp, readdir, readFile, rm } from \"node:fs/promises\";\n\nimport { join, relative } from \"pathe\";\n\nimport { mountBasePath, stripBasePath } from \"./base-path.ts\";\nimport { nextFenceState } from \"./code-fences.ts\";\nimport type { FenceState } from \"./code-fences.ts\";\nimport { writeTextAtomic } from \"./fs-atomic.ts\";\nimport { localizeRoute } from \"./i18n.ts\";\nimport type { BlumeProject } from \"./project-graph.ts\";\nimport { isWithin, scanProject } from \"./project-graph.ts\";\nimport { VERSION_ID } from \"./schema.ts\";\nimport type { Diagnostic } from \"./types.ts\";\nimport { VERSION_LIKE, versionizeRoute } from \"./versions.ts\";\n\n/** What `cutVersion` did, for the CLI to report. */\nexport interface CutResult {\n /** Files copied into the snapshot. */\n copied: number;\n /** Markdown pages whose root-absolute links were rewritten, with counts. */\n rewritten: { file: string; count: number }[];\n /** Whether `blume.config.ts` was updated in place. */\n configUpdated: boolean;\n /**\n * Whether that update added the whole `versions` block — the first cut of a\n * project that had no versioning — rather than one `archived` entry.\n */\n versionsAdded: boolean;\n /** Ready-to-paste config snippet when in-place update was not possible. */\n configSnippet: string | null;\n /** Absolute path of the created snapshot directory. */\n dir: string;\n}\n\n/** A `cutVersion` failure the CLI reports as a user error, not a crash. */\nexport class CutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CutError\";\n }\n}\n\n// Inline code spans are syntax being *shown*, not links — blank them before\n// matching so replacements can splice into the real line by index (the mask\n// preserves length). Mirrors the content-assets rewriter.\nconst INLINE_CODE = /`[^`]*`/gu;\n// A root-absolute markdown link/image target, reference-style link definition\n// (`[setup]: /guides/setup`, up to three spaces in, never a `[^note]:`\n// footnote), or HTML href/src attribute in either quote style. Named groups\n// carry the prefix (kept) and target (rewritten).\nconst ROOT_LINK =\n /(?<prefix>\\]\\(|^ {0,3}\\[(?!\\^)[^\\]]+\\]:[ \\t]*<?|(?:href|src)=[\"'])(?<target>\\/[^\\s\"'<>)]*)/gu;\n\n/**\n * Build the link-rewrite table: every current-version route (basePath\n * stripped, since authors write root-absolute links as if mounted at root;\n * a link that spells the base out is looked up without it, see\n * `snapshotTarget`) mapped to the same page's route inside the new snapshot.\n * Only pages the snapshot actually contains qualify — filesystem pages under\n * the content root. Spec-rendered references (`/api`, `/events`) and remote\n * sources aren't copied, so links to them keep pointing at the live pages\n * instead of a 404 inside the snapshot.\n */\nconst buildRouteRewrites = (\n project: BlumeProject,\n id: string\n): Map<string, string> => {\n const { basePath, i18n } = project.config;\n const { contentRoot } = project.context;\n const rewrites = new Map<string, string>();\n // Under i18n, authors write links in the unprefixed form (`/guides/setup`)\n // and rendering moves them into the reader's locale — a form no page is\n // routed at when the default locale keeps its prefix\n // (`hideDefaultLocalePrefix: false`). So it is mapped too, to the\n // unprefixed snapshot route, which rendering localizes the same way. Page\n // routes win where the two forms coincide.\n const logical = new Map<string, string>();\n for (const page of project.graph.pages) {\n // `sourcePath` marks a page backed by a local file, which excludes\n // generated and remote pages; the content-root check below excludes local\n // files that live outside the tree a version snapshot copies. A staged\n // page (an Obsidian note) is served from its source, not from the copied\n // tree, so it keeps publishing as current even when its file sits under\n // the content root.\n const { sourcePath } = page;\n if (\n page.version !== \"\" ||\n page.collection === \"staged\" ||\n !sourcePath ||\n relative(contentRoot, sourcePath).startsWith(\"..\")\n ) {\n continue;\n }\n const snapshot = versionizeRoute(page.versionKey, id);\n rewrites.set(\n stripBasePath(basePath, page.route),\n i18n ? localizeRoute(snapshot, page.locale, i18n) : snapshot\n );\n if (i18n) {\n logical.set(page.versionKey, snapshot);\n }\n }\n for (const [route, snapshot] of logical) {\n if (!rewrites.has(route)) {\n rewrites.set(route, snapshot);\n }\n }\n return rewrites;\n};\n\n/**\n * The snapshot link a root-absolute path rewrites to, or undefined when the\n * snapshot has no copy of its page. A path written with the base path by hand\n * (`/docs/guides/x` under `basePath: \"/docs\"`) names the same page as\n * `/guides/x` — rendering leaves an already-based link alone (see\n * `withBasePath`) — so it is looked up without the base and keeps it.\n */\nconst snapshotTarget = (\n path: string,\n rewrites: Map<string, string>,\n basePath: string\n): string | undefined => {\n const based =\n basePath !== \"\" && (path === basePath || path.startsWith(`${basePath}/`));\n if (!based) {\n return rewrites.get(path);\n }\n const replacement = rewrites.get(stripBasePath(basePath, path));\n return replacement === undefined\n ? undefined\n : mountBasePath(basePath, replacement);\n};\n\n/** Rewrite one line's root-absolute internal links via the rewrite table. */\nconst rewriteLine = (\n line: string,\n rewrites: Map<string, string>,\n basePath: string\n): string => {\n const masked = line.replaceAll(INLINE_CODE, (span) =>\n \" \".repeat(span.length)\n );\n let out = \"\";\n let cursor = 0;\n let count = 0;\n for (const match of masked.matchAll(ROOT_LINK)) {\n const target = match.groups?.target ?? \"\";\n // An anchor or query stays attached to the rewritten path.\n const hash = target.search(/[#?]/u);\n const path = hash === -1 ? target : target.slice(0, hash);\n const suffix = hash === -1 ? \"\" : target.slice(hash);\n const bare = path !== \"/\" && path.endsWith(\"/\") ? path.slice(0, -1) : path;\n const replacement = snapshotTarget(bare, rewrites, basePath);\n if (replacement === undefined) {\n continue;\n }\n const offset = (match.index ?? 0) + (match.groups?.prefix?.length ?? 0);\n out += line.slice(cursor, offset) + replacement + suffix;\n cursor = offset + target.length;\n count += 1;\n }\n return count === 0 ? line : out + line.slice(cursor);\n};\n\n/** A page's link-rewritten text and how many of its lines changed. */\nexport interface SnapshotRewrite {\n text: string;\n count: number;\n}\n\n/**\n * Rewrite a copied page's root-absolute internal links to their snapshot\n * equivalents, skipping fenced and inline code. Relative links need no\n * rewriting — the whole tree copies together, so they stay self-contained.\n * `basePath` is the site's, for links that spell it out by hand.\n */\nexport const rewriteSnapshotLinks = (\n source: string,\n rewrites: Map<string, string>,\n basePath = \"\"\n): SnapshotRewrite => {\n let fence: FenceState = null;\n let count = 0;\n const lines = source.split(\"\\n\").map((line) => {\n const next = nextFenceState(line, fence);\n const inFence = fence !== null || next !== null;\n fence = next;\n if (inFence) {\n return line;\n }\n const rewrittenLine = rewriteLine(line, rewrites, basePath);\n if (rewrittenLine !== line) {\n count += 1;\n }\n return rewrittenLine;\n });\n return { count, text: lines.join(\"\\n\") };\n};\n\n/**\n * The switcher label a first cut gives the live docs. `versions.current.label`\n * is required, and \"Latest\" reads right until the author names the release.\n */\nconst CURRENT_LABEL = \"Latest\";\n\n/** The config entry to add, as a paste-ready snippet for the fallback path. */\nconst snippetFor = (id: string, hasVersions: boolean): string =>\n hasVersions\n ? `Add to versions.archived in blume.config.ts (newest first):\\n\\n { id: \"${id}\" },\\n`\n : `Add to blume.config.ts:\\n\\n versions: {\\n archived: [{ id: \"${id}\" }],\\n current: { label: \"${CURRENT_LABEL}\" },\\n },\\n`;\n\n/**\n * Best-effort in-place config update: insert the new id at the head of an\n * existing \\`archived: [\\` array literal, preserving indentation. Anything\n * fancier (computed arrays, config spread across files) falls back to a\n * printed snippet — the config is user-authored TypeScript, and string-level\n * surgery beyond this simple shape risks corrupting it.\n */\nexport const insertArchivedVersion = async (\n configPath: string,\n id: string\n): Promise<boolean> => {\n let text: string;\n try {\n text = await readFile(configPath, \"utf-8\");\n } catch {\n return false;\n }\n const match = text.match(/(?<lead>archived:\\s*\\[)/u);\n if (!match || match.index === undefined) {\n return false;\n }\n const insertAt = match.index + (match.groups?.lead?.length ?? 0);\n // Indentation: one level deeper than the line holding `archived:`.\n const lineStart = text.lastIndexOf(\"\\n\", match.index) + 1;\n const indent = text.slice(lineStart).match(/^\\s*/u)?.[0] ?? \"\";\n const rest = text.slice(insertAt);\n // Match the array's authored shape: empty stays bare, an inline array gets\n // an inline entry, a multiline array gets its own indented line — on the\n // file's own line ending, so a CRLF config doesn't gain a lone LF.\n const eol = /^\\r?\\n/u.exec(rest)?.[0];\n let entry: string;\n if (rest.trimStart().startsWith(\"]\")) {\n entry = `{ id: \"${id}\" }`;\n } else if (eol) {\n entry = `${eol}${indent} { id: \"${id}\" },`;\n } else {\n entry = `{ id: \"${id}\" }, `;\n }\n await writeTextAtomic(configPath, text.slice(0, insertAt) + entry + rest);\n return true;\n};\n\n/** The opening brace of the config object: `defineConfig({` or `export default {`. */\nconst CONFIG_OBJECT = /(?:defineConfig\\(\\s*|export\\s+default\\s+)\\{/gu;\n\n/**\n * Best-effort in-place registration of a first version: add a `versions` block\n * (the id archived, the live docs labeled {@link CURRENT_LABEL}) as the first\n * property of the config object. Only the plain shape is edited — exactly one\n * `defineConfig({` or `export default {` and no `versions` key yet — so a\n * config built any other way falls back to the printed snippet rather than\n * risk corrupting it.\n */\nexport const insertVersionsBlock = async (\n configPath: string,\n id: string\n): Promise<boolean> => {\n let text: string;\n try {\n text = await readFile(configPath, \"utf-8\");\n } catch {\n return false;\n }\n const matches = [...text.matchAll(CONFIG_OBJECT)];\n const [match] = matches;\n if (matches.length !== 1 || !match || /\\bversions\\s*:/u.test(text)) {\n return false;\n }\n const insertAt = match.index + match[0].length;\n const rest = text.slice(insertAt);\n const eol = text.includes(\"\\r\\n\") ? \"\\r\\n\" : \"\\n\";\n const lineStart = text.lastIndexOf(\"\\n\", match.index) + 1;\n const outer = /^[ \\t]*/u.exec(text.slice(lineStart))?.[0] ?? \"\";\n // Match the indentation of the property on the next line, when there is one.\n const indent =\n /^[ \\t]*\\r?\\n(?<indent>[ \\t]*)\\S/u.exec(rest)?.groups?.indent ??\n `${outer} `;\n const block = [\n \"versions: {\",\n ` archived: [{ id: \"${id}\" }],`,\n ` current: { label: \"${CURRENT_LABEL}\" },`,\n \"},\",\n ]\n .map((line) => `${indent}${line}`)\n .join(eol);\n const body = rest.trimStart();\n let tail: string;\n if (body.startsWith(\"}\")) {\n // An empty object: close it on its own line.\n tail = `${eol}${outer}${body}`;\n } else if (/^[ \\t]*\\r?\\n/u.test(rest)) {\n tail = rest;\n } else {\n // An inline object: move its first property onto the next line.\n tail = `${eol}${indent}${body}`;\n }\n await writeTextAtomic(\n configPath,\n `${text.slice(0, insertAt)}${eol}${block}${tail}`\n );\n return true;\n};\n\n/**\n * Version-shaped folders (`v1.0/`) at the top of the content root of a project\n * with no `versions` config — usually a snapshot `blume version` cut but\n * couldn't register — which build as ordinary content at `/v1.0/…`, so every\n * page ships twice. With versioning configured, the scan's own\n * `BLUME_VERSIONS_UNCONFIGURED_VERSION` covers a folder it doesn't list; this\n * check is `blume doctor`'s alone, since an unversioned site can have a real\n * `v1/` section that a warning on every build would only nag about.\n */\nexport const unregisteredSnapshotDiagnostics = (\n project: BlumeProject\n): Diagnostic[] => {\n if (project.config.versions) {\n return [];\n }\n const { contentRoot } = project.context;\n const folders = new Set<string>();\n for (const page of project.graph.pages) {\n const [first, ...rest] = page.sourcePath\n ? relative(contentRoot, page.sourcePath).split(\"/\")\n : [];\n if (first && rest.length > 0 && VERSION_LIKE.test(first)) {\n folders.add(first);\n }\n }\n return [...folders].toSorted().map((folder) => ({\n code: \"BLUME_VERSIONS_UNCONFIGURED_VERSION\",\n file: join(contentRoot, folder),\n message: `Folder \"${folder}/\" looks like a version snapshot, but versioning isn't configured, so its pages build as ordinary content at /${folder}/….`,\n severity: \"warning\",\n suggestion: `If it's a snapshot, register it with \\`versions: { archived: [{ id: \"${folder}\" }], current: { label: \"${CURRENT_LABEL}\" } }\\` in blume.config.ts; if it's an ordinary section, ignore this.`,\n }));\n};\n\n/**\n * Freeze the current docs as an archived version: copy the content tree into\n * `<contentRoot>/<id>/` (excluding existing snapshots), rewrite root-absolute\n * internal links so the copy is self-contained, and register the id in\n * `blume.config.ts` (or print the snippet to paste).\n */\nexport const cutVersion = async (\n root: string,\n id: string,\n options: { force?: boolean } = {}\n): Promise<CutResult> => {\n if (!VERSION_ID.test(id)) {\n throw new CutError(\n `Version ids must start with a letter (e.g. \"v1.0\") and contain only letters, digits, dots, hyphens, and underscores — got \"${id}\".`\n );\n }\n\n const project = await scanProject(root);\n const errors = project.diagnostics.filter(\n (diagnostic) => diagnostic.severity === \"error\"\n );\n if (errors.length > 0) {\n throw new CutError(\n `The project has ${errors.length} error diagnostic(s) — fix them before cutting a version (run \\`blume validate\\`).`\n );\n }\n // A missing content root already fails inside `scanProject` (the filesystem\n // source's own validation), so only the version-level checks remain here.\n const { contentRoot } = project.context;\n if (project.config.versions?.archived.some((version) => version.id === id)) {\n throw new CutError(\n `Version \"${id}\" is already registered in versions.archived.`\n );\n }\n\n const dir = join(contentRoot, id);\n if (existsSync(dir)) {\n if (!options.force) {\n throw new CutError(\n `${dir} already exists — pass --force to overwrite the snapshot.`\n );\n }\n await rm(dir, { force: true, recursive: true });\n }\n\n // Existing snapshots must not nest inside the new one: configured ids, plus\n // any version-shaped directory (`v1.0/`) that never made it into the config —\n // e.g. a prior cut whose config update fell back to a printed snippet. A\n // genuine content folder that merely looks like a version already draws the\n // rename-or-register diagnostic, so skipping it here is consistent.\n const excluded = new Set([\n id,\n \"node_modules\",\n ...(project.config.versions?.archived.map((version) => version.id) ?? []),\n ]);\n // Another local source's tree under the content root — an Obsidian vault the\n // filesystem source excludes — is that source's to publish. Copying it would\n // republish its raw notes (wikilinks and `%%comments%%` intact) as\n // filesystem pages of the snapshot, while the source itself keeps serving\n // them as current.\n const foreignRoots = project.sources\n .flatMap((source) =>\n source.staged && source.contentRoot ? [source.contentRoot] : []\n )\n .filter((tree) => isWithin(contentRoot, tree));\n const isForeign = (path: string): boolean =>\n foreignRoots.some((tree) => isWithin(tree, path));\n const entries = await readdir(contentRoot, { withFileTypes: true });\n let copied = 0;\n await Promise.all(\n entries.map(async (entry) => {\n if (\n excluded.has(entry.name) ||\n entry.name.startsWith(\".\") ||\n (entry.isDirectory() && VERSION_LIKE.test(entry.name))\n ) {\n return;\n }\n await cp(join(contentRoot, entry.name), join(dir, entry.name), {\n filter: (source) => !isForeign(source),\n recursive: true,\n });\n })\n );\n\n // Rewrite root-absolute internal links in every copied markdown page.\n const rewrites = buildRouteRewrites(project, id);\n const rewritten: { file: string; count: number }[] = [];\n const walk = async (current: string): Promise<void> => {\n const children = await readdir(current, { withFileTypes: true });\n await Promise.all(\n children.map(async (child) => {\n const abs = join(current, child.name);\n if (child.isDirectory()) {\n await walk(abs);\n return;\n }\n copied += 1;\n if (!/\\.mdx?$/u.test(child.name)) {\n return;\n }\n const source = await readFile(abs, \"utf-8\");\n const { text, count } = rewriteSnapshotLinks(\n source,\n rewrites,\n project.config.basePath\n );\n if (count > 0) {\n await writeTextAtomic(abs, text);\n rewritten.push({ count, file: relative(dir, abs) });\n }\n })\n );\n };\n await walk(dir);\n\n const configPath =\n project.context.configFile ?? join(root, \"blume.config.ts\");\n const firstVersion = project.config.versions === undefined;\n const configUpdated = firstVersion\n ? await insertVersionsBlock(configPath, id)\n : await insertArchivedVersion(configPath, id);\n\n return {\n configSnippet: configUpdated ? null : snippetFor(id, !firstVersion),\n configUpdated,\n copied,\n dir,\n rewritten,\n versionsAdded: configUpdated && firstVersion,\n };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8631919BA7BA49B664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|