blume 2.0.1 → 2.0.3
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/CHANGELOG.md +147 -0
- package/dist/cli/{chunk-qwsrynx5.js → chunk-1d7ve1dm.js} +38 -19
- package/dist/cli/{chunk-qwsrynx5.js.map → chunk-1d7ve1dm.js.map} +4 -4
- package/dist/cli/{chunk-s6jhgk0q.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-kdp5q7ke.js → chunk-2hsdwb9n.js} +18 -19
- package/dist/cli/{chunk-kdp5q7ke.js.map → chunk-2hsdwb9n.js.map} +2 -2
- package/dist/cli/{chunk-hdpx1tax.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-jwyddg7y.js → chunk-35d4wj9f.js} +30 -24
- package/dist/cli/chunk-35d4wj9f.js.map +15 -0
- package/dist/cli/{chunk-ah61y8py.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-jts8mvcz.js → chunk-3em5wd2y.js} +25 -6
- package/dist/cli/{chunk-jts8mvcz.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-s1p84fyh.js → chunk-5f86nr5m.js} +63 -22
- package/dist/cli/chunk-5f86nr5m.js.map +11 -0
- package/dist/cli/{chunk-2hn4b8z7.js → chunk-5m5nmvyq.js} +170 -76
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-epjnccmv.js → chunk-5xvm6tfj.js} +18 -15
- package/dist/cli/chunk-5xvm6tfj.js.map +10 -0
- package/dist/cli/{chunk-fz5wtpmh.js → chunk-6dsbexzp.js} +18 -15
- package/dist/cli/chunk-6dsbexzp.js.map +10 -0
- package/dist/cli/{chunk-vtk4a6dg.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-6hsn950k.js → chunk-9t7a85s3.js} +178 -48
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-fxypxtvm.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-mb2919y2.js → chunk-acanzt5p.js} +17 -6
- package/dist/cli/chunk-acanzt5p.js.map +10 -0
- package/dist/cli/{chunk-27g6wdth.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-wm7js3j9.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-wgm7m9qk.js → chunk-c8chx29p.js} +179 -51
- package/dist/cli/chunk-c8chx29p.js.map +36 -0
- package/dist/cli/{chunk-qs4q5p4e.js → chunk-crgn1q09.js} +32 -14
- package/dist/cli/chunk-crgn1q09.js.map +10 -0
- package/dist/cli/chunk-e04dxsz1.js +39 -0
- package/dist/cli/chunk-e04dxsz1.js.map +10 -0
- package/dist/cli/{chunk-yt5n7ppj.js → chunk-ey84smr6.js} +17 -7
- package/dist/cli/chunk-ey84smr6.js.map +10 -0
- package/dist/cli/{chunk-f2z5v128.js → chunk-g4hq16wv.js} +14 -15
- package/dist/cli/{chunk-f2z5v128.js.map → chunk-g4hq16wv.js.map} +2 -2
- package/dist/cli/{chunk-fa25z98p.js → chunk-ga0pf4aj.js} +9 -8
- package/dist/cli/chunk-ga0pf4aj.js.map +11 -0
- package/dist/cli/{chunk-kpf8rrjc.js → chunk-hm3vjy5s.js} +109 -58
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-fs23ddbb.js → chunk-j85vccga.js} +625 -750
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-ch6g3ar0.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-ch6g3ar0.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-q5163e60.js → chunk-p73c0m7w.js} +21 -19
- package/dist/cli/chunk-p73c0m7w.js.map +11 -0
- package/dist/cli/{chunk-dh8cwk36.js → chunk-pehfxfta.js} +24 -9
- package/dist/cli/chunk-pehfxfta.js.map +10 -0
- package/dist/cli/{chunk-qkqwkpte.js → chunk-pv29h0wf.js} +3688 -1313
- package/dist/cli/chunk-pv29h0wf.js.map +190 -0
- package/dist/cli/{chunk-6vm74dry.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6vm74dry.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-zxcczpyx.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-m3vmjgmq.js → chunk-r9rcc4w7.js} +16 -8
- package/dist/cli/chunk-r9rcc4w7.js.map +10 -0
- package/dist/cli/{chunk-5shv93fd.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-zxh4d9vy.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-yw7dm696.js → chunk-vg9r4eb9.js} +24 -11
- package/dist/cli/chunk-vg9r4eb9.js.map +14 -0
- package/dist/cli/{chunk-79jhk4py.js → chunk-wrr3j9w9.js} +260 -132
- package/dist/cli/chunk-wrr3j9w9.js.map +35 -0
- package/dist/cli/{chunk-6crbhc3x.js → chunk-yfyb25rh.js} +54 -27
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +162 -35
- package/dist/cli/index.js.map +4 -4
- package/dist/types/ai/agent-surface.d.ts +32 -0
- package/dist/types/ai/api-catalog.d.ts +7 -1
- package/dist/types/ai/ask-context.d.ts +7 -0
- package/dist/types/ai/component-markdown.d.ts +4 -4
- 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 +11 -4
- package/dist/types/ai/skills.d.ts +4 -1
- package/dist/types/ai/static-expression.d.ts +28 -0
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/databuddy.d.ts +43 -0
- package/dist/types/analytics/index.d.ts +4 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +28 -0
- package/dist/types/astro/integration.d.ts +3 -2
- package/dist/types/cli/env.d.ts +5 -0
- package/dist/types/cli/init/scaffold.d.ts +19 -3
- package/dist/types/cli/init/starter-spec.d.ts +11 -0
- package/dist/types/core/base-path.d.ts +24 -1
- package/dist/types/core/config-input.d.ts +13 -11
- package/dist/types/core/config.d.ts +2 -2
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/graph.d.ts +2 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +31 -0
- package/dist/types/core/i18n.d.ts +9 -1
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/links.d.ts +3 -1
- package/dist/types/core/load-module.d.ts +10 -0
- package/dist/types/core/locale-links.d.ts +12 -2
- package/dist/types/core/meta.d.ts +13 -1
- package/dist/types/core/nav-diagnostics.d.ts +10 -0
- package/dist/types/core/navigation.d.ts +38 -0
- package/dist/types/core/ordering-prefix.d.ts +4 -0
- package/dist/types/core/safe-links.d.ts +3 -1
- package/dist/types/core/schema.d.ts +59 -13
- package/dist/types/core/sources/github-releases.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +38 -13
- package/dist/types/core/sources/normalize.d.ts +23 -2
- package/dist/types/core/sources/remote.d.ts +11 -1
- package/dist/types/core/sources/resolve.d.ts +12 -0
- package/dist/types/core/sources/types.d.ts +31 -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 +9 -0
- package/dist/types/deploy/adapters/node.d.ts +5 -2
- package/dist/types/deploy/adapters/types.d.ts +7 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
- package/dist/types/deploy/headers.d.ts +36 -7
- package/dist/types/deploy/node-headers.d.ts +43 -8
- package/dist/types/deploy/platforms/netlify.d.ts +27 -2
- package/dist/types/deploy/platforms/node.d.ts +6 -5
- package/dist/types/deploy/platforms/types.d.ts +15 -0
- package/dist/types/deploy/platforms/vercel.d.ts +3 -2
- package/dist/types/deploy/redirects.d.ts +31 -15
- package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/markdown/mdast.d.ts +63 -0
- package/dist/types/openapi/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +20 -6
- package/dist/types/search/sync/algolia.d.ts +3 -1
- package/docs/01-quickstart.mdx +3 -2
- package/docs/02-deployment.mdx +20 -9
- package/docs/08-faq.mdx +10 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +7 -5
- package/docs/cli/audit.mdx +19 -3
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +4 -1
- package/docs/cli/translate.mdx +4 -4
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +42 -2
- package/docs/configuration/assistant.mdx +1 -1
- package/docs/configuration/customization.mdx +6 -3
- package/docs/configuration/index.mdx +5 -3
- package/docs/configuration/search.mdx +2 -2
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -1
- package/docs/content/i18n.mdx +3 -1
- package/docs/content/index.mdx +8 -4
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +7 -3
- package/docs/content/navigation.mdx +29 -4
- package/docs/content/sources.mdx +18 -16
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +2 -1
- package/docs/discoverability/agent-discovery.mdx +22 -8
- package/docs/discoverability/index.mdx +4 -3
- package/docs/discoverability/llms-txt.mdx +2 -5
- package/docs/discoverability/markdown.mdx +6 -4
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +3 -2
- package/docs/discoverability/open-graph.mdx +8 -4
- package/docs/discoverability/rss.mdx +4 -2
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +2 -2
- package/docs/references/openapi.mdx +6 -4
- package/package.json +1 -1
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +6 -6
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +3 -3
- package/skills/blume-migrate/references/monorepo.md +1 -1
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +5 -5
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +9 -4
- package/skills/blume-update-docs/references/audit-checklist.md +1 -1
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/agent-surface.ts +56 -0
- package/src/ai/ai-catalog.ts +6 -2
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +17 -4
- package/src/ai/api-catalog.ts +13 -3
- package/src/ai/ask-context.ts +14 -2
- package/src/ai/ask-data.ts +26 -12
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +43 -37
- package/src/ai/link-headers.ts +22 -8
- package/src/ai/llms.ts +22 -10
- package/src/ai/markdown.ts +23 -11
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +80 -9
- package/src/ai/openapi-components.ts +22 -6
- package/src/ai/relative-links.ts +99 -15
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/static-expression.ts +416 -0
- package/src/ai/tar.ts +139 -12
- package/src/ai/visibility.ts +45 -14
- package/src/analytics/databuddy.ts +67 -0
- package/src/analytics/head.ts +8 -0
- package/src/analytics/index.ts +7 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/posthog.ts +21 -3
- package/src/analytics/schema.ts +4 -0
- package/src/astro/generate.ts +72 -28
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/integration.ts +108 -14
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +139 -57
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +25 -5
- package/src/audit/checks/content.ts +20 -2
- package/src/audit/checks/i18n.ts +46 -8
- package/src/audit/checks/indexability.ts +39 -19
- package/src/audit/checks/links.ts +22 -1
- package/src/audit/checks/llms.ts +6 -3
- package/src/audit/checks/network.ts +3 -1
- package/src/audit/checks/og-image.ts +10 -0
- package/src/audit/checks/robots.ts +6 -1
- package/src/audit/checks/sitemap.ts +56 -31
- package/src/audit/checks/social.ts +21 -2
- package/src/audit/crawl.ts +88 -15
- package/src/audit/graph.ts +3 -1
- package/src/audit/report.ts +54 -17
- package/src/audit/run.ts +13 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +18 -0
- package/src/audit/url.ts +37 -6
- package/src/cli/args.ts +32 -0
- package/src/cli/build-failure.ts +50 -0
- package/src/cli/commands/audit.ts +11 -1
- package/src/cli/commands/build.ts +19 -5
- package/src/cli/commands/check.ts +2 -0
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +20 -18
- package/src/cli/commands/init.ts +15 -5
- package/src/cli/commands/preview.ts +15 -0
- package/src/cli/commands/sync.ts +2 -0
- package/src/cli/commands/translate.ts +18 -18
- package/src/cli/commands/upgrade.ts +11 -0
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/eject-scripts.ts +32 -7
- package/src/cli/env.ts +12 -1
- package/src/cli/init/scaffold.ts +117 -11
- package/src/cli/init/starter-spec.ts +235 -0
- package/src/cli/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/AccordionItem.astro +26 -22
- package/src/components/content/Badge.astro +7 -12
- package/src/components/content/Card.astro +2 -2
- package/src/components/content/Component.astro +11 -5
- package/src/components/content/Expandable.astro +5 -1
- package/src/components/content/Frame.astro +2 -7
- package/src/components/content/GithubInfo.astro +12 -2
- package/src/components/content/Prompt.astro +2 -7
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +58 -5
- package/src/components/content/Tile.astro +1 -1
- package/src/components/content/Tooltip.astro +69 -7
- package/src/components/content/Tree.astro +7 -2
- package/src/components/content/TypeTable.astro +10 -5
- package/src/components/content/Update.astro +8 -2
- package/src/components/content/auto-type-table.ts +4 -1
- package/src/components/content/badge-color.ts +20 -0
- package/src/components/content/base-href.ts +14 -22
- package/src/components/content/inline-markdown.ts +27 -7
- package/src/components/copy-feedback.ts +35 -8
- package/src/components/islands/assistant.tsx +37 -6
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +42 -21
- package/src/components/islands/webmcp.ts +16 -10
- package/src/components/layout/Banner.astro +23 -4
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +23 -10
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/Logo.astro +5 -0
- package/src/components/layout/NavSelector.astro +8 -3
- package/src/components/layout/NavTabMenu.astro +133 -0
- package/src/components/layout/NavTree.astro +31 -12
- package/src/components/layout/NavTreeCache.astro +5 -2
- package/src/components/layout/NavTreeScript.astro +45 -5
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageLayout.astro +28 -13
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +4 -1
- package/src/components/layout/RootLayout.astro +25 -12
- package/src/components/layout/Search.astro +38 -14
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +14 -0
- 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/GraphqlType.astro +11 -3
- package/src/components/openapi/MessageComposer.astro +1 -1
- package/src/components/openapi/Operation.astro +23 -5
- package/src/components/openapi/PanelTabs.astro +4 -1
- package/src/components/openapi/Playground.astro +8 -2
- package/src/components/openapi/RequestPanel.astro +13 -6
- package/src/components/openapi/SchemaProperty.astro +11 -33
- package/src/components/openapi/SchemaTable.astro +34 -52
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/helpers.ts +100 -12
- package/src/components/openapi/message-composer.ts +14 -1
- package/src/components/openapi/message-model.ts +12 -2
- package/src/components/openapi/message.ts +21 -3
- package/src/components/openapi/operation-model.ts +119 -17
- package/src/components/openapi/panel.ts +31 -5
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +110 -23
- package/src/components/openapi/playground-schema.ts +25 -6
- package/src/components/openapi/request.ts +192 -26
- package/src/components/openapi/schema-tree.ts +209 -0
- package/src/components/openapi/snippets.ts +113 -19
- package/src/components/openapi/validate-json.ts +1 -1
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +69 -8
- package/src/core/config-input.ts +13 -11
- package/src/core/config.ts +18 -4
- package/src/core/diagnostics.ts +219 -30
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +120 -11
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +39 -2
- package/src/core/i18n.ts +44 -5
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +7 -4
- package/src/core/load-module.ts +20 -0
- package/src/core/locale-links.ts +22 -18
- package/src/core/manifest.ts +3 -2
- package/src/core/meta.ts +89 -12
- package/src/core/nav-diagnostics.ts +56 -1
- package/src/core/navigation.ts +272 -83
- package/src/core/ordering-prefix.ts +27 -0
- package/src/core/project-graph.ts +50 -6
- package/src/core/safe-href.ts +53 -1
- package/src/core/safe-links.ts +11 -2
- package/src/core/schema.ts +94 -18
- package/src/core/sources/assets.ts +83 -41
- package/src/core/sources/contentful-rich-text.ts +28 -20
- package/src/core/sources/contentful.ts +25 -12
- package/src/core/sources/filesystem.ts +25 -3
- package/src/core/sources/github-releases.ts +131 -11
- package/src/core/sources/lexical.ts +30 -20
- package/src/core/sources/lower.ts +276 -56
- package/src/core/sources/mdx-remote.ts +52 -16
- package/src/core/sources/normalize.ts +456 -119
- package/src/core/sources/notion.ts +77 -34
- package/src/core/sources/obsidian.ts +40 -9
- package/src/core/sources/payload.ts +1 -0
- package/src/core/sources/portable-text.ts +85 -49
- package/src/core/sources/remote.ts +18 -2
- package/src/core/sources/resolve.ts +52 -33
- package/src/core/sources/sanity.ts +1 -0
- package/src/core/sources/strapi-blocks.ts +28 -19
- package/src/core/sources/strapi.ts +1 -0
- package/src/core/sources/types.ts +31 -0
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +9 -0
- package/src/core/ui-packs/ar.ts +13 -0
- package/src/core/ui-packs/bg.ts +13 -0
- package/src/core/ui-packs/bn.ts +13 -0
- package/src/core/ui-packs/ca.ts +13 -0
- package/src/core/ui-packs/cs.ts +13 -0
- package/src/core/ui-packs/da.ts +13 -0
- package/src/core/ui-packs/de.ts +13 -0
- package/src/core/ui-packs/el.ts +13 -0
- package/src/core/ui-packs/es.ts +13 -0
- package/src/core/ui-packs/fa.ts +13 -0
- package/src/core/ui-packs/fi.ts +13 -0
- package/src/core/ui-packs/fr.ts +13 -0
- package/src/core/ui-packs/he.ts +13 -0
- package/src/core/ui-packs/hi.ts +13 -0
- package/src/core/ui-packs/hr.ts +13 -0
- package/src/core/ui-packs/hu.ts +13 -0
- package/src/core/ui-packs/id.ts +13 -0
- package/src/core/ui-packs/it.ts +13 -0
- package/src/core/ui-packs/ja.ts +13 -0
- package/src/core/ui-packs/ko.ts +13 -0
- package/src/core/ui-packs/nl.ts +13 -0
- package/src/core/ui-packs/no.ts +13 -0
- package/src/core/ui-packs/pl.ts +13 -0
- package/src/core/ui-packs/pt-br.ts +13 -0
- package/src/core/ui-packs/pt.ts +13 -0
- package/src/core/ui-packs/ro.ts +13 -0
- package/src/core/ui-packs/ru.ts +13 -0
- package/src/core/ui-packs/sk.ts +13 -0
- package/src/core/ui-packs/sr.ts +13 -0
- package/src/core/ui-packs/sv.ts +13 -0
- package/src/core/ui-packs/th.ts +13 -0
- package/src/core/ui-packs/tr.ts +13 -0
- package/src/core/ui-packs/uk.ts +13 -0
- package/src/core/ui-packs/vi.ts +13 -0
- package/src/core/ui-packs/zh-tw.ts +13 -0
- package/src/core/ui-packs/zh.ts +13 -0
- package/src/core/version-cut.ts +69 -17
- package/src/core/versions.ts +4 -1
- package/src/deploy/adapters/node.ts +5 -2
- package/src/deploy/adapters/registry.ts +2 -1
- package/src/deploy/adapters/types.ts +13 -1
- package/src/deploy/artifacts.ts +52 -11
- package/src/deploy/cloudflare-negotiation.ts +27 -16
- package/src/deploy/headers.ts +75 -55
- package/src/deploy/node-headers.ts +148 -27
- package/src/deploy/platforms/cloudflare.ts +13 -3
- package/src/deploy/platforms/netlify.ts +83 -5
- package/src/deploy/platforms/node.ts +8 -5
- package/src/deploy/platforms/static.ts +2 -0
- package/src/deploy/platforms/types.ts +15 -0
- package/src/deploy/platforms/vercel.ts +10 -3
- package/src/deploy/redirects.ts +68 -21
- 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 +35 -3
- package/src/eval/agents.ts +10 -2
- package/src/eval/run.ts +25 -0
- package/src/markdown/base-links.ts +74 -29
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +13 -9
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +15 -27
- package/src/markdown/route-snapshot.ts +37 -0
- package/src/og/card.ts +149 -9
- package/src/og/derive.ts +156 -5
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/graphql-build.ts +28 -2
- package/src/openapi/model.ts +99 -27
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +51 -3
- package/src/registry/eject.ts +297 -43
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +89 -27
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +36 -2
- package/src/seo/jsonld.ts +7 -3
- package/src/sources/registry.ts +5 -0
- package/src/theme/entry.ts +11 -4
- package/src/translate/agents.ts +6 -1
- package/src/translate/ledger.ts +26 -3
- package/src/translate/meta.ts +76 -24
- package/src/translate/run.ts +14 -8
- package/src/translate/validate.ts +14 -2
- package/src/translate/work-list.ts +91 -19
- package/src/upgrade/upgrade.ts +36 -4
- package/dist/cli/chunk-27g6wdth.js.map +0 -10
- package/dist/cli/chunk-2hn4b8z7.js.map +0 -12
- package/dist/cli/chunk-6crbhc3x.js.map +0 -14
- package/dist/cli/chunk-6hsn950k.js.map +0 -10
- package/dist/cli/chunk-79jhk4py.js.map +0 -35
- package/dist/cli/chunk-82bbrxdn.js +0 -51
- package/dist/cli/chunk-82bbrxdn.js.map +0 -10
- package/dist/cli/chunk-abh8yjkn.js +0 -31
- package/dist/cli/chunk-abh8yjkn.js.map +0 -10
- package/dist/cli/chunk-ce574jw2.js +0 -23
- package/dist/cli/chunk-ce574jw2.js.map +0 -10
- package/dist/cli/chunk-dh8cwk36.js.map +0 -10
- package/dist/cli/chunk-epjnccmv.js.map +0 -10
- package/dist/cli/chunk-fa25z98p.js.map +0 -11
- package/dist/cli/chunk-fs23ddbb.js.map +0 -35
- package/dist/cli/chunk-fz5wtpmh.js.map +0 -10
- package/dist/cli/chunk-jwyddg7y.js.map +0 -15
- package/dist/cli/chunk-kpf8rrjc.js.map +0 -19
- package/dist/cli/chunk-m3vmjgmq.js.map +0 -10
- package/dist/cli/chunk-mb2919y2.js.map +0 -10
- package/dist/cli/chunk-q5163e60.js.map +0 -11
- package/dist/cli/chunk-qkqwkpte.js.map +0 -182
- package/dist/cli/chunk-qs4q5p4e.js.map +0 -10
- package/dist/cli/chunk-s1p84fyh.js.map +0 -10
- package/dist/cli/chunk-wgm7m9qk.js.map +0 -36
- package/dist/cli/chunk-yt5n7ppj.js.map +0 -10
- package/dist/cli/chunk-yw7dm696.js.map +0 -14
- /package/dist/cli/{chunk-s6jhgk0q.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-hdpx1tax.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-ah61y8py.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-vtk4a6dg.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-fxypxtvm.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-wm7js3j9.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-zxcczpyx.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-5shv93fd.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-zxh4d9vy.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -7,8 +7,10 @@ import { join } from "pathe";
|
|
|
7
7
|
import { loadConfig } from "../../core/config.ts";
|
|
8
8
|
import { resolveProjectContext } from "../../core/project.ts";
|
|
9
9
|
import { deployOutputDir } from "../../deploy/adapter-output.ts";
|
|
10
|
+
import { deployPlatform } from "../../deploy/platforms/index.ts";
|
|
10
11
|
import { parsePort } from "../args.ts";
|
|
11
12
|
import { commandMeta } from "../command-meta.ts";
|
|
13
|
+
import { refuseIfEjected } from "../eject-scripts.ts";
|
|
12
14
|
import { normalizeHost } from "../host-args.ts";
|
|
13
15
|
import { logger } from "../log.ts";
|
|
14
16
|
|
|
@@ -20,9 +22,22 @@ export const previewCommand = defineCommand({
|
|
|
20
22
|
meta: commandMeta.preview,
|
|
21
23
|
async run({ args }) {
|
|
22
24
|
const root = process.cwd();
|
|
25
|
+
await refuseIfEjected(root, "preview");
|
|
23
26
|
const { config } = await loadConfig(root);
|
|
24
27
|
const context = resolveProjectContext(root, config);
|
|
25
28
|
|
|
29
|
+
// Astro previews a server build through its adapter's preview entrypoint,
|
|
30
|
+
// and the Vercel and Netlify adapters declare none, so Astro would throw
|
|
31
|
+
// mid-command. Say so up front, with what to run instead.
|
|
32
|
+
const { deployment } = config;
|
|
33
|
+
const platform = deployPlatform(deployment);
|
|
34
|
+
if (deployment.options.output === "server" && platform.previewDeploy) {
|
|
35
|
+
logger.error(
|
|
36
|
+
`\`blume preview\` can't serve a ${deployment.kind}() server build: ${platform.astro?.package} has no local preview server. Run \`blume dev\` to try the site locally, or deploy a preview with \`${platform.previewDeploy}\`.`
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
// `blume dev` writes `.blume/astro.config.mjs` too, so the runtime alone
|
|
27
42
|
// doesn't mean there's anything to serve: the build output must exist.
|
|
28
43
|
if (
|
package/src/cli/commands/sync.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../core/project.ts";
|
|
11
11
|
import { commandMeta } from "../command-meta.ts";
|
|
12
12
|
import { readDevLock } from "../dev-lock.ts";
|
|
13
|
+
import { refuseIfEjected } from "../eject-scripts.ts";
|
|
13
14
|
import { logger } from "../log.ts";
|
|
14
15
|
import { prepareProject } from "../prepare.ts";
|
|
15
16
|
|
|
@@ -28,6 +29,7 @@ export const syncCommand = defineCommand({
|
|
|
28
29
|
meta: commandMeta.sync,
|
|
29
30
|
async run({ args }) {
|
|
30
31
|
const root = process.cwd();
|
|
32
|
+
await refuseIfEjected(root, "sync");
|
|
31
33
|
|
|
32
34
|
// `--force` drops the snapshots so a stale or corrupt cache can't be served;
|
|
33
35
|
// scoped to `.blume/cache`, so a running dev server's runtime is untouched.
|
|
@@ -4,7 +4,6 @@ import { AGENTS } from "../../audit/agent.ts";
|
|
|
4
4
|
import type { AgentKind } from "../../audit/agent.ts";
|
|
5
5
|
import { BlumeError } from "../../core/diagnostics.ts";
|
|
6
6
|
import { i18nEnabled, localeCodes } from "../../core/i18n.ts";
|
|
7
|
-
import { scanProject } from "../../core/project-graph.ts";
|
|
8
7
|
import type { ResolvedI18nConfig } from "../../core/schema.ts";
|
|
9
8
|
import { DEFAULT_TRANSLATE_TIMEOUT_MS } from "../../translate/agents.ts";
|
|
10
9
|
import {
|
|
@@ -25,10 +24,14 @@ import {
|
|
|
25
24
|
translateSummaryLine,
|
|
26
25
|
} from "../../translate/report.ts";
|
|
27
26
|
import { runTranslate } from "../../translate/run.ts";
|
|
28
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
computeWorkList,
|
|
29
|
+
scanForTranslation,
|
|
30
|
+
} from "../../translate/work-list.ts";
|
|
31
|
+
import { parseTimeoutSeconds } from "../args.ts";
|
|
29
32
|
import { commandMeta } from "../command-meta.ts";
|
|
30
33
|
import { reportInternalError } from "../internal-error.ts";
|
|
31
|
-
import {
|
|
34
|
+
import { logger, reportDiagnostics } from "../log.ts";
|
|
32
35
|
|
|
33
36
|
/** Wall-clock ceiling per file, in seconds. */
|
|
34
37
|
const DEFAULT_TIMEOUT_S = DEFAULT_TRANSLATE_TIMEOUT_MS / 1000;
|
|
@@ -78,12 +81,7 @@ const parseFlags = (args: TranslateFlags): ParsedTranslateFlags => {
|
|
|
78
81
|
);
|
|
79
82
|
process.exit(1);
|
|
80
83
|
}
|
|
81
|
-
const timeoutS =
|
|
82
|
-
args.timeout === undefined ? DEFAULT_TIMEOUT_S : Number(args.timeout);
|
|
83
|
-
if (!Number.isInteger(timeoutS) || timeoutS <= 0) {
|
|
84
|
-
logger.error(`Invalid --timeout "${args.timeout}" (whole seconds).`);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
84
|
+
const timeoutS = parseTimeoutSeconds(args.timeout, DEFAULT_TIMEOUT_S);
|
|
87
85
|
const concurrency =
|
|
88
86
|
args.concurrency === undefined
|
|
89
87
|
? DEFAULT_CONCURRENCY
|
|
@@ -188,10 +186,11 @@ export const translateCommand = defineCommand({
|
|
|
188
186
|
const { agent, concurrency, timeoutS } = parseFlags(args);
|
|
189
187
|
|
|
190
188
|
try {
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
|
|
189
|
+
// A scan, not `prepareProject`: translation reads the content tree and
|
|
190
|
+
// writes source files, never `.blume/`, so it doesn't contend with a
|
|
191
|
+
// running dev server. Same reasoning as `blume audit`/`eval`. Drafts
|
|
192
|
+
// are included (see `scanForTranslation`).
|
|
193
|
+
const project = await scanForTranslation(root);
|
|
195
194
|
if (!i18nEnabled(project.config)) {
|
|
196
195
|
logger.error(
|
|
197
196
|
"i18n is not configured — add `i18n.locales` to blume.config to use `blume translate`."
|
|
@@ -225,8 +224,9 @@ export const translateCommand = defineCommand({
|
|
|
225
224
|
process.stdout.write(checkReportJson(workList));
|
|
226
225
|
}
|
|
227
226
|
if (hasDrift(workList)) {
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
// Set the code and return rather than `process.exit`, which waits for
|
|
228
|
+
// neither a piped stderr (the drift report) nor a piped stdout.
|
|
229
|
+
process.exitCode = 1;
|
|
230
230
|
}
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
@@ -287,9 +287,9 @@ export const translateCommand = defineCommand({
|
|
|
287
287
|
}
|
|
288
288
|
if (result.counts.failed > 0 || result.counts.partial > 0) {
|
|
289
289
|
// The ledger write above already persisted every success, so a failed
|
|
290
|
-
// rerun only retries what actually failed.
|
|
291
|
-
|
|
292
|
-
process.
|
|
290
|
+
// rerun only retries what actually failed. Set the code rather than
|
|
291
|
+
// `process.exit`, which would cut off a piped stderr summary.
|
|
292
|
+
process.exitCode = 1;
|
|
293
293
|
}
|
|
294
294
|
} catch (error) {
|
|
295
295
|
if (error instanceof BlumeError) {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
bumpBlumeDependency,
|
|
13
13
|
collectUpgradeFindings,
|
|
14
14
|
isOutsideBlumeProject,
|
|
15
|
+
manualBumpAdvice,
|
|
15
16
|
upgradePrompt,
|
|
16
17
|
} from "../../upgrade/upgrade.ts";
|
|
17
18
|
import { commandMeta } from "../command-meta.ts";
|
|
@@ -94,10 +95,20 @@ export const upgradeCommand = defineCommand({
|
|
|
94
95
|
logger.warn(
|
|
95
96
|
`No \`blume\` dependency in ./package.json; add blume@^${version} to the package that uses it.`
|
|
96
97
|
);
|
|
98
|
+
} else if (bump.status === "manual") {
|
|
99
|
+
logger.warn(manualBumpAdvice(bump.range, version));
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
const findings = await collectUpgradeFindings(root);
|
|
100
103
|
if (findings.length === 0) {
|
|
104
|
+
// Until the range the bump couldn't rewrite moves, the project still
|
|
105
|
+
// installs the old Blume, so it isn't ready yet.
|
|
106
|
+
if (bump.status === "manual") {
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
` The config, components.ts, and pages check out for Blume ${version}. Update blume's version as above, reinstall, then run \`${runner} blume build\` to confirm.\n`
|
|
109
|
+
);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
101
112
|
process.stderr.write(
|
|
102
113
|
` Ready for Blume ${version}: the config, components.ts, and pages check out. Run \`${runner} blume build\` to confirm.\n`
|
|
103
114
|
);
|
|
@@ -119,7 +119,9 @@ export const validateCommand = defineCommand({
|
|
|
119
119
|
logger.success("No broken links found.");
|
|
120
120
|
}
|
|
121
121
|
if (hadErrors || strictFailure) {
|
|
122
|
-
process.exit
|
|
122
|
+
// Set the code and return rather than `process.exit`, which doesn't wait
|
|
123
|
+
// for a piped stderr: a long diagnostic list would be cut off.
|
|
124
|
+
process.exitCode = 1;
|
|
123
125
|
}
|
|
124
126
|
},
|
|
125
127
|
});
|
package/src/cli/dev-lock.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
existsSync,
|
|
3
|
+
linkSync,
|
|
3
4
|
mkdirSync,
|
|
4
5
|
readFileSync,
|
|
6
|
+
renameSync,
|
|
5
7
|
rmSync,
|
|
8
|
+
statSync,
|
|
6
9
|
writeFileSync,
|
|
7
10
|
} from "node:fs";
|
|
8
11
|
|
|
@@ -29,6 +32,83 @@ export interface DevLockInfo {
|
|
|
29
32
|
|
|
30
33
|
const lockPath = (outDir: string): string => join(outDir, "dev.lock");
|
|
31
34
|
|
|
35
|
+
/** The mutex a process holds while it clears a stale lock. */
|
|
36
|
+
const reapPath = (outDir: string): string => join(outDir, "dev.lock.reap");
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* How long a lock file that doesn't parse counts as one being written right
|
|
40
|
+
* now. Blume creates a lock with its whole content in place (see
|
|
41
|
+
* {@link createExclusive}), but one left empty or cut off — by a crash, or by
|
|
42
|
+
* an older Blume that opened the file before writing it — would otherwise
|
|
43
|
+
* read as stale the instant it appears. Past this age it's debris.
|
|
44
|
+
*/
|
|
45
|
+
const PARTIAL_LOCK_GRACE_MS = 2000;
|
|
46
|
+
|
|
47
|
+
/** Pause between claim attempts while another process is mid-claim. */
|
|
48
|
+
const RETRY_DELAY_MS = 5;
|
|
49
|
+
|
|
50
|
+
const pause = (ms: number): void => {
|
|
51
|
+
// A synchronous sleep: the claim is synchronous, and what it waits on is
|
|
52
|
+
// another process, so there's nothing in this one to yield to.
|
|
53
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Per-process counter that keeps concurrent temp file names apart. */
|
|
57
|
+
let tempSequence = 0;
|
|
58
|
+
|
|
59
|
+
/** A private sibling of `path` to stage a write in. */
|
|
60
|
+
const tempPath = (path: string): string => {
|
|
61
|
+
tempSequence += 1;
|
|
62
|
+
return `${path}.${process.pid}.${tempSequence}.tmp`;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create `path` holding `payload`, atomically and exclusively. The payload
|
|
67
|
+
* goes to a private temp file first, which is then hard-linked into place:
|
|
68
|
+
* the link fails when `path` exists, exactly like a `wx` open, but the file
|
|
69
|
+
* appears with its whole content — no reader ever sees it empty or half
|
|
70
|
+
* written. Returns false when `path` already exists.
|
|
71
|
+
*/
|
|
72
|
+
const createExclusive = (path: string, payload: string): boolean => {
|
|
73
|
+
const temp = tempPath(path);
|
|
74
|
+
writeFileSync(temp, payload);
|
|
75
|
+
try {
|
|
76
|
+
linkSync(temp, path);
|
|
77
|
+
return true;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// SAFETY: `linkSync` failures are errno exceptions.
|
|
80
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") {
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
} finally {
|
|
85
|
+
rmSync(temp, { force: true });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Replace `path` with `payload` in one step, so readers see old or new. */
|
|
90
|
+
const replaceFile = (path: string, payload: string): void => {
|
|
91
|
+
const temp = tempPath(path);
|
|
92
|
+
writeFileSync(temp, payload);
|
|
93
|
+
renameSync(temp, path);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** A file's text, or null when it doesn't exist (anymore). */
|
|
97
|
+
const readIfExists = (
|
|
98
|
+
path: string
|
|
99
|
+
): { raw: string; mtimeMs: number } | null => {
|
|
100
|
+
try {
|
|
101
|
+
const raw = readFileSync(path, "utf-8");
|
|
102
|
+
return { mtimeMs: statSync(path).mtimeMs, raw };
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// SAFETY: `readFileSync`/`statSync` failures are errno exceptions.
|
|
105
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
32
112
|
/** What `JSON.parse` can yield for a lock file body. */
|
|
33
113
|
type LockFileValue =
|
|
34
114
|
| string
|
|
@@ -85,17 +165,44 @@ const isProcessAlive = (pid: number): boolean => {
|
|
|
85
165
|
}
|
|
86
166
|
};
|
|
87
167
|
|
|
168
|
+
/** What the lock file on a dir says, read once. */
|
|
169
|
+
type LockState =
|
|
170
|
+
| { kind: "absent" }
|
|
171
|
+
/** Unparseable but brand new: another process is writing it right now. */
|
|
172
|
+
| { kind: "busy" }
|
|
173
|
+
| { kind: "live"; lock: DevLockInfo }
|
|
174
|
+
/** A dead holder's lock, or unparseable debris. */
|
|
175
|
+
| { kind: "stale" };
|
|
176
|
+
|
|
177
|
+
const inspectLock = (outDir: string): LockState => {
|
|
178
|
+
const file = readIfExists(lockPath(outDir));
|
|
179
|
+
if (!file) {
|
|
180
|
+
return { kind: "absent" };
|
|
181
|
+
}
|
|
182
|
+
const lock = parseLock(file.raw);
|
|
183
|
+
if (!lock) {
|
|
184
|
+
return Date.now() - file.mtimeMs < PARTIAL_LOCK_GRACE_MS
|
|
185
|
+
? { kind: "busy" }
|
|
186
|
+
: { kind: "stale" };
|
|
187
|
+
}
|
|
188
|
+
return isProcessAlive(lock.pid) ? { kind: "live", lock } : { kind: "stale" };
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Whether this process may clear the lock to claim the dir: a dead holder's
|
|
193
|
+
* lock and debris, or this process's own leftover.
|
|
194
|
+
*/
|
|
195
|
+
const isClaimable = (state: LockState): boolean =>
|
|
196
|
+
state.kind === "stale" ||
|
|
197
|
+
(state.kind === "live" && state.lock.pid === process.pid);
|
|
198
|
+
|
|
88
199
|
/**
|
|
89
200
|
* Read the lock on `outDir` held by a live `blume dev`, or null. A lock left
|
|
90
201
|
* by a process that has since exited (stale) is treated as absent.
|
|
91
202
|
*/
|
|
92
203
|
export const readDevLock = (outDir: string): DevLockInfo | null => {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const lock = parseLock(readFileSync(path, "utf-8"));
|
|
98
|
-
return lock && isProcessAlive(lock.pid) ? lock : null;
|
|
204
|
+
const state = inspectLock(outDir);
|
|
205
|
+
return state.kind === "live" ? state.lock : null;
|
|
99
206
|
};
|
|
100
207
|
|
|
101
208
|
/** Whether another live `blume dev` holds the lock on `outDir`. */
|
|
@@ -110,10 +217,6 @@ const lockPayload = (port?: number): string => {
|
|
|
110
217
|
return JSON.stringify(info);
|
|
111
218
|
};
|
|
112
219
|
|
|
113
|
-
const writeLock = (outDir: string, port?: number): void => {
|
|
114
|
-
writeFileSync(lockPath(outDir), lockPayload(port));
|
|
115
|
-
};
|
|
116
|
-
|
|
117
220
|
/** Thrown when another live `blume dev` already holds the lock. */
|
|
118
221
|
export class DevLockHeldError extends Error {
|
|
119
222
|
readonly lock: DevLockInfo;
|
|
@@ -138,43 +241,58 @@ const ownsLock = (outDir: string): boolean => {
|
|
|
138
241
|
};
|
|
139
242
|
|
|
140
243
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
244
|
+
* Clear a claimable lock without racing anyone else doing the same. Two
|
|
245
|
+
* processes can both read the same stale lock; if each simply deleted the
|
|
246
|
+
* file, the slower one could delete the lock the faster one had claimed in
|
|
247
|
+
* between, and both would win. So removing a lock takes the reap mutex
|
|
248
|
+
* (created the same atomic way), and whoever holds it reads the lock again
|
|
249
|
+
* first: a live lock claimed since is left alone. A mutex whose holder died
|
|
250
|
+
* mid-reap is cleared for the next attempt.
|
|
145
251
|
*/
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
252
|
+
const reapStaleLock = (outDir: string): void => {
|
|
253
|
+
const mutex = reapPath(outDir);
|
|
254
|
+
if (!createExclusive(mutex, lockPayload())) {
|
|
255
|
+
const holder = readIfExists(mutex);
|
|
256
|
+
const pid = holder ? parseLock(holder.raw)?.pid : undefined;
|
|
257
|
+
if (pid !== undefined && pid !== process.pid && isProcessAlive(pid)) {
|
|
258
|
+
pause(RETRY_DELAY_MS);
|
|
259
|
+
} else if (holder) {
|
|
260
|
+
rmSync(mutex, { force: true });
|
|
154
261
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
if (isClaimable(inspectLock(outDir))) {
|
|
266
|
+
rmSync(lockPath(outDir), { force: true });
|
|
158
267
|
}
|
|
159
|
-
|
|
160
|
-
rmSync(
|
|
161
|
-
return false;
|
|
268
|
+
} finally {
|
|
269
|
+
rmSync(mutex, { force: true });
|
|
162
270
|
}
|
|
163
271
|
};
|
|
164
272
|
|
|
165
273
|
/**
|
|
166
274
|
* Write the current process's dev lock into `outDir` and return a release
|
|
167
|
-
* function.
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
* this process's own leftover is
|
|
171
|
-
*
|
|
172
|
-
*
|
|
275
|
+
* function. Two `blume dev` processes starting at the same moment can't both
|
|
276
|
+
* win: the lock appears whole and exclusively (a hard link, which fails when
|
|
277
|
+
* the file exists), a lock still being written is waited on rather than read
|
|
278
|
+
* as stale, and a stale lock (dead pid) or this process's own leftover is only
|
|
279
|
+
* cleared under the reap mutex — the loser gets a {@link DevLockHeldError}
|
|
280
|
+
* naming the live holder. The release only removes the file if it's still
|
|
281
|
+
* ours, so a newer dev server's lock is never clobbered.
|
|
173
282
|
*/
|
|
174
283
|
export const acquireDevLock = (outDir: string, port?: number): (() => void) => {
|
|
175
284
|
mkdirSync(outDir, { recursive: true });
|
|
176
|
-
|
|
177
|
-
|
|
285
|
+
const payload = lockPayload(port);
|
|
286
|
+
while (!createExclusive(lockPath(outDir), payload)) {
|
|
287
|
+
const state = inspectLock(outDir);
|
|
288
|
+
if (isClaimable(state)) {
|
|
289
|
+
reapStaleLock(outDir);
|
|
290
|
+
} else if (state.kind === "live") {
|
|
291
|
+
throw new DevLockHeldError(state.lock);
|
|
292
|
+
} else if (state.kind === "busy") {
|
|
293
|
+
pause(RETRY_DELAY_MS);
|
|
294
|
+
}
|
|
295
|
+
// "absent": the holder released it just now — race for the claim again.
|
|
178
296
|
}
|
|
179
297
|
let released = false;
|
|
180
298
|
return () => {
|
|
@@ -199,7 +317,9 @@ export const acquireDevLock = (outDir: string, port?: number): (() => void) => {
|
|
|
199
317
|
*/
|
|
200
318
|
export const updateDevLockPort = (outDir: string, port: number): void => {
|
|
201
319
|
if (ownsLock(outDir)) {
|
|
202
|
-
|
|
320
|
+
// Replaced in one step: rewriting in place would leave a moment where a
|
|
321
|
+
// concurrent claim reads an empty lock.
|
|
322
|
+
replaceFile(lockPath(outDir), lockPayload(port));
|
|
203
323
|
}
|
|
204
324
|
};
|
|
205
325
|
|
package/src/cli/eject-scripts.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from "pathe";
|
|
|
6
6
|
import { EJECTED_CONFIG_HEADER } from "../astro/templates.ts";
|
|
7
7
|
import { packageRoot } from "../core/package-root.ts";
|
|
8
8
|
import { commandsFor, detectProjectPackageManager } from "./init/scaffold.ts";
|
|
9
|
+
import type { PackageManager } from "./init/scaffold.ts";
|
|
9
10
|
import { logger } from "./log.ts";
|
|
10
11
|
|
|
11
12
|
/** A JSON value, as `JSON.parse` of a manifest can return. */
|
|
@@ -136,22 +137,46 @@ export const isEjectedProject = (root: string): boolean => {
|
|
|
136
137
|
);
|
|
137
138
|
};
|
|
138
139
|
|
|
140
|
+
/** The Blume commands that run the hidden `.blume/` copy of the site. */
|
|
141
|
+
export type RuntimeCommand = "build" | "check" | "dev" | "preview" | "sync";
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* What an ejected app runs in place of each {@link RuntimeCommand}: its own
|
|
145
|
+
* `dev`/`build`/`preview` scripts (eject writes them), or Astro's CLI for the
|
|
146
|
+
* check and the content sync.
|
|
147
|
+
*/
|
|
148
|
+
const ejectedEquivalent = (
|
|
149
|
+
pm: PackageManager,
|
|
150
|
+
command: RuntimeCommand
|
|
151
|
+
): string => {
|
|
152
|
+
const { build, dev, exec } = commandsFor(pm);
|
|
153
|
+
return {
|
|
154
|
+
build,
|
|
155
|
+
check: `${exec} astro check`,
|
|
156
|
+
dev,
|
|
157
|
+
// Spelled like `dev`: npm runs a custom script only through `run`.
|
|
158
|
+
preview: pm === "npm" ? "npm run preview" : `${pm} preview`,
|
|
159
|
+
sync: `${exec} astro sync`,
|
|
160
|
+
}[command];
|
|
161
|
+
};
|
|
162
|
+
|
|
139
163
|
/**
|
|
140
|
-
* Stop
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
164
|
+
* Stop a command that works on the hidden runtime in an ejected app. `dev`,
|
|
165
|
+
* `build`, `check`, and `sync` would regenerate `.blume/` from
|
|
166
|
+
* `blume.config.ts` and run that hidden copy, ignoring every edit made to the
|
|
167
|
+
* app since eject, and `preview` would look for that copy's build; point at
|
|
168
|
+
* the app's own command under the project's package manager instead.
|
|
144
169
|
*/
|
|
145
170
|
export const refuseIfEjected = async (
|
|
146
171
|
root: string,
|
|
147
|
-
|
|
172
|
+
command: RuntimeCommand
|
|
148
173
|
): Promise<void> => {
|
|
149
174
|
if (!isEjectedProject(root)) {
|
|
150
175
|
return;
|
|
151
176
|
}
|
|
152
|
-
const
|
|
177
|
+
const pm = await detectProjectPackageManager(root);
|
|
153
178
|
logger.error(
|
|
154
|
-
`This project was ejected to a standalone Astro app, so \`blume ${
|
|
179
|
+
`This project was ejected to a standalone Astro app, so \`blume ${command}\` would run a regenerated copy of the site instead of it. Run \`${ejectedEquivalent(pm, command)}\` instead.`
|
|
155
180
|
);
|
|
156
181
|
process.exit(1);
|
|
157
182
|
};
|
package/src/cli/env.ts
CHANGED
|
@@ -19,6 +19,11 @@ import { dirname, join, resolve } from "pathe";
|
|
|
19
19
|
* these files at build time, so a value means the same thing to the pre-boot
|
|
20
20
|
* content scan and the built site — including multi-line double-quoted values
|
|
21
21
|
* (PEM keys), which a line-based parser silently truncates.
|
|
22
|
+
*
|
|
23
|
+
* `NODE_ENV` is never taken from these files: it picks the mode Vite and Astro
|
|
24
|
+
* run in, so a monorepo root `.env` that sets it for another app would turn
|
|
25
|
+
* every `blume build` into a development build (or `blume dev` into a
|
|
26
|
+
* production one).
|
|
22
27
|
*/
|
|
23
28
|
export const loadEnvFiles = (startDir: string): void => {
|
|
24
29
|
const paths: string[] = [];
|
|
@@ -31,5 +36,11 @@ export const loadEnvFiles = (startDir: string): void => {
|
|
|
31
36
|
done = existsSync(join(dir, ".git")) || parent === dir;
|
|
32
37
|
dir = parent;
|
|
33
38
|
}
|
|
34
|
-
|
|
39
|
+
const loaded: Record<string, string> = {};
|
|
40
|
+
config({ path: paths, processEnv: loaded, quiet: true });
|
|
41
|
+
for (const [key, value] of Object.entries(loaded)) {
|
|
42
|
+
if (key !== "NODE_ENV" && process.env[key] === undefined) {
|
|
43
|
+
process.env[key] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
};
|