blume 2.0.2 → 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 +65 -0
- package/dist/cli/{chunk-6k4ftwze.js → chunk-1d7ve1dm.js} +1 -1
- package/dist/cli/{chunk-zp79m0ts.js → chunk-2eytanqx.js} +2 -2
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-2hsdwb9n.js} +19 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-2hsdwb9n.js.map} +1 -1
- package/dist/cli/{chunk-g698a744.js → chunk-2z928egk.js} +5 -5
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-35d4wj9f.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-35d4wj9f.js.map} +3 -3
- package/dist/cli/{chunk-mqc662a6.js → chunk-364znk6q.js} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-3em5wd2y.js} +21 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-3em5wd2y.js.map} +3 -3
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5f86nr5m.js} +15 -15
- package/dist/cli/{chunk-hr8ne106.js → chunk-5m5nmvyq.js} +68 -43
- package/dist/cli/chunk-5m5nmvyq.js.map +13 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-5xvm6tfj.js} +14 -14
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-6dsbexzp.js} +14 -14
- package/dist/cli/{chunk-00gs3wqs.js → chunk-8ktnccpt.js} +1 -1
- package/dist/cli/{chunk-j85scx15.js → chunk-9t7a85s3.js} +130 -44
- package/dist/cli/chunk-9t7a85s3.js.map +10 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-a58773jm.js} +2 -2
- package/dist/cli/{chunk-nfcyttvj.js → chunk-acanzt5p.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-acanzt5p.js.map} +1 -1
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-akbpwfxc.js} +90 -26
- package/dist/cli/chunk-akbpwfxc.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-b07cmahc.js} +2 -2
- package/dist/cli/{chunk-xh43dwgw.js → chunk-c8chx29p.js} +42 -29
- package/dist/cli/{chunk-xh43dwgw.js.map → chunk-c8chx29p.js.map} +9 -9
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-crgn1q09.js} +21 -12
- 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-4e9b9ra6.js → chunk-ey84smr6.js} +3 -3
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-g4hq16wv.js} +13 -13
- 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-pbg5a4s3.js → chunk-hm3vjy5s.js} +64 -45
- package/dist/cli/chunk-hm3vjy5s.js.map +19 -0
- package/dist/cli/{chunk-5r8g91qn.js → chunk-j85vccga.js} +306 -107
- package/dist/cli/chunk-j85vccga.js.map +36 -0
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-p3v96n38.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-p3v96n38.js.map} +3 -3
- package/dist/cli/{chunk-7vtckvaw.js → chunk-p73c0m7w.js} +14 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-p73c0m7w.js.map} +1 -1
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-pehfxfta.js} +3 -3
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-pv29h0wf.js} +1223 -978
- package/dist/cli/{chunk-v6ya5kcb.js.map → chunk-pv29h0wf.js.map} +47 -46
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-q58y5e6a.js} +10 -10
- package/dist/cli/{chunk-6dtt0zfn.js.map → chunk-q58y5e6a.js.map} +3 -3
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-r20tn01b.js} +1 -1
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-r9rcc4w7.js} +7 -7
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-tkacnehg.js} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-tzmab476.js} +4 -4
- package/dist/cli/{chunk-3yce002v.js → chunk-vg9r4eb9.js} +6 -6
- package/dist/cli/{chunk-3yce002v.js.map → chunk-vg9r4eb9.js.map} +4 -4
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-wrr3j9w9.js} +16 -17
- package/dist/cli/{chunk-hqp2ajnh.js.map → chunk-wrr3j9w9.js.map} +7 -7
- package/dist/cli/{chunk-ddndchfr.js → chunk-yfyb25rh.js} +38 -26
- package/dist/cli/chunk-yfyb25rh.js.map +14 -0
- package/dist/cli/index.js +20 -18
- package/dist/cli/index.js.map +3 -3
- 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/skills.d.ts +4 -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 +3 -2
- package/dist/types/core/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +9 -7
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/schema.d.ts +7 -0
- 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/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/redirects.d.ts +24 -13
- 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/docs/02-deployment.mdx +4 -2
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/cli/audit.mdx +17 -1
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +20 -1
- package/docs/configuration/customization.mdx +2 -0
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/components.mdx +1 -1
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/i18n.mdx +2 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +3 -1
- package/docs/content/sources.mdx +1 -5
- package/docs/content/syntax.mdx +14 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +2 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/asyncapi.mdx +1 -1
- package/docs/references/graphql.mdx +1 -1
- package/docs/references/openapi.mdx +5 -3
- package/package.json +1 -1
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/nextra.md +1 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +7 -2
- package/src/ai/agent-readability.ts +2 -2
- package/src/ai/ai-catalog.ts +6 -2
- 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/changelog-markdown.ts +2 -2
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +18 -7
- 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 +22 -6
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/skills.ts +14 -1
- package/src/ai/tar.ts +139 -12
- 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 +1 -1
- 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 +81 -37
- package/src/audit/checks/links.ts +8 -1
- package/src/audit/graph.ts +3 -1
- package/src/audit/run.ts +12 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/cli/args.ts +32 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +3 -1
- package/src/cli/commands/eval.ts +19 -11
- 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/report-format.ts +11 -6
- package/src/components/colors.ts +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Component.astro +2 -2
- package/src/components/content/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- package/src/components/islands/assistant.tsx +21 -4
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/hooks.ts +35 -19
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Breadcrumbs.astro +5 -2
- package/src/components/layout/DiscoveryLinks.astro +9 -5
- package/src/components/layout/Header.astro +2 -2
- package/src/components/layout/LanguageSwitcher.astro +2 -2
- package/src/components/layout/NavSelector.astro +2 -2
- 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/PageLayout.astro +7 -6
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/RootLayout.astro +5 -5
- package/src/components/layout/Search.astro +33 -13
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -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/Operation.astro +2 -0
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestPanel.astro +5 -2
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async.ts +38 -6
- 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/snippets.ts +85 -6
- package/src/components/openapi/ws-client.ts +18 -2
- package/src/core/base-path.ts +62 -16
- package/src/core/config-input.ts +9 -7
- package/src/core/diagnostics.ts +2 -0
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n.ts +31 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/locale-links.ts +5 -1
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +28 -8
- package/src/core/project-graph.ts +27 -5
- package/src/core/schema.ts +24 -4
- 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 +325 -90
- 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/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +27 -6
- package/src/deploy/cloudflare-negotiation.ts +4 -12
- package/src/deploy/headers.ts +8 -4
- package/src/deploy/node-headers.ts +1 -1
- package/src/deploy/platforms/cloudflare.ts +12 -3
- package/src/deploy/platforms/netlify.ts +1 -0
- 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 +1 -0
- package/src/deploy/redirects.ts +54 -18
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +17 -0
- package/src/markdown/index.ts +7 -6
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- package/src/og/card.ts +129 -5
- 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 +22 -14
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/render-mdx.ts +41 -2
- package/src/registry/eject.ts +181 -24
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +67 -26
- package/src/search/popular.ts +2 -1
- package/src/seo/jsonld.ts +7 -3
- package/src/translate/meta.ts +68 -24
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +4 -1
- package/src/translate/work-list.ts +56 -17
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- 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-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- /package/dist/cli/{chunk-6k4ftwze.js.map → chunk-1d7ve1dm.js.map} +0 -0
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-2eytanqx.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-2z928egk.js.map} +0 -0
- /package/dist/cli/{chunk-mqc662a6.js.map → chunk-364znk6q.js.map} +0 -0
- /package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5f86nr5m.js.map} +0 -0
- /package/dist/cli/{chunk-91ws1n6j.js.map → chunk-5xvm6tfj.js.map} +0 -0
- /package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-6dsbexzp.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-8ktnccpt.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-a58773jm.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-b07cmahc.js.map} +0 -0
- /package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-ey84smr6.js.map} +0 -0
- /package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-g4hq16wv.js.map} +0 -0
- /package/dist/cli/{chunk-qkb5a8sa.js.map → chunk-pehfxfta.js.map} +0 -0
- /package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-r20tn01b.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-r9rcc4w7.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-tkacnehg.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-tzmab476.js.map} +0 -0
|
@@ -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-pv29h0wf.js";
|
|
12
12
|
import {
|
|
13
|
+
mountBasePath,
|
|
13
14
|
stripBasePath
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3em5wd2y.js";
|
|
15
16
|
import {
|
|
16
17
|
isWithin,
|
|
17
18
|
scanProject
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-j85vccga.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-crgn1q09.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
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger
|
|
4
|
+
} from "./index.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/args.ts
|
|
7
|
+
var MAX_PORT = 65535;
|
|
8
|
+
var parsePort = (value) => {
|
|
9
|
+
if (value === undefined) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const port = Number(value);
|
|
13
|
+
if (!(Number.isInteger(port) && port >= 1 && port <= MAX_PORT)) {
|
|
14
|
+
logger.error(`Invalid --port "${value}" (expected an integer 1-${MAX_PORT}).`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
return port;
|
|
18
|
+
};
|
|
19
|
+
var MAX_TIMEOUT_S = Math.floor(2147483647 / 1000);
|
|
20
|
+
var parseTimeoutSeconds = (value, fallback) => {
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
return fallback;
|
|
23
|
+
}
|
|
24
|
+
const timeout = Number(value);
|
|
25
|
+
if (!Number.isInteger(timeout) || timeout <= 0) {
|
|
26
|
+
logger.error(`Invalid --timeout "${value}" (whole seconds).`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
if (timeout > MAX_TIMEOUT_S) {
|
|
30
|
+
logger.error(`Invalid --timeout "${value}" (at most ${MAX_TIMEOUT_S} seconds, about 24 days).`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
return timeout;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { parsePort, parseTimeoutSeconds };
|
|
37
|
+
|
|
38
|
+
//# debugId=02AD79A562BBAEE664756E2164756E21
|
|
39
|
+
//# sourceMappingURL=chunk-e04dxsz1.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/args.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { logger } from \"./log.ts\";\n\nconst MAX_PORT = 65_535;\n\n/**\n * Parse a `--port` value into a valid port number, or `undefined` when unset.\n * A non-integer or out-of-range value (`--port abc` → `NaN`) exits with an\n * error rather than propagating `localhost:NaN` into the dev server and the\n * `deployment.site` fallback.\n */\nexport const parsePort = (value?: string): number | undefined => {\n if (value === undefined) {\n return;\n }\n const port = Number(value);\n if (!(Number.isInteger(port) && port >= 1 && port <= MAX_PORT)) {\n logger.error(\n `Invalid --port \"${value}\" (expected an integer 1-${MAX_PORT}).`\n );\n process.exit(1);\n }\n return port;\n};\n\n/**\n * The longest `--timeout` a timer can hold, in seconds. Node clamps a\n * `setTimeout` delay above 2^31−1 ms (about 24.8 days) to 1 ms, so a larger\n * value would time every agent out the moment it starts.\n */\nexport const MAX_TIMEOUT_S = Math.floor(2_147_483_647 / 1000);\n\n/**\n * Parse a `--timeout` value in whole seconds, or return `fallback` when unset.\n * A non-integer, non-positive, or too-long value exits with an error.\n */\nexport const parseTimeoutSeconds = (\n value: string | undefined,\n fallback: number\n): number => {\n if (value === undefined) {\n return fallback;\n }\n const timeout = Number(value);\n if (!Number.isInteger(timeout) || timeout <= 0) {\n logger.error(`Invalid --timeout \"${value}\" (whole seconds).`);\n process.exit(1);\n }\n if (timeout > MAX_TIMEOUT_S) {\n logger.error(\n `Invalid --timeout \"${value}\" (at most ${MAX_TIMEOUT_S} seconds, about 24 days).`\n );\n process.exit(1);\n }\n return timeout;\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;AAEA,IAAM,WAAW;AAQV,IAAM,YAAY,CAAC,UAAuC;AAAA,EAC/D,IAAI,UAAU,WAAW;AAAA,IACvB;AAAA,EACF;AAAA,EACA,MAAM,OAAO,OAAO,KAAK;AAAA,EACzB,IAAI,EAAE,OAAO,UAAU,IAAI,KAAK,QAAQ,KAAK,QAAQ,WAAW;AAAA,IAC9D,OAAO,MACL,mBAAmB,iCAAiC,YACtD;AAAA,IACA,QAAQ,KAAK,CAAC;AAAA,EAChB;AAAA,EACA,OAAO;AAAA;AAQF,IAAM,gBAAgB,KAAK,MAAM,aAAgB,IAAI;AAMrD,IAAM,sBAAsB,CACjC,OACA,aACW;AAAA,EACX,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO;AAAA,EACT;AAAA,EACA,MAAM,UAAU,OAAO,KAAK;AAAA,EAC5B,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,WAAW,GAAG;AAAA,IAC9C,OAAO,MAAM,sBAAsB,yBAAyB;AAAA,IAC5D,QAAQ,KAAK,CAAC;AAAA,EAChB;AAAA,EACA,IAAI,UAAU,eAAe;AAAA,IAC3B,OAAO,MACL,sBAAsB,mBAAmB,wCAC3C;AAAA,IACA,QAAQ,KAAK,CAAC;AAAA,EAChB;AAAA,EACA,OAAO;AAAA;",
|
|
8
|
+
"debugId": "02AD79A562BBAEE664756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commandsFor,
|
|
4
4
|
detectProjectPackageManager
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-pv29h0wf.js";
|
|
6
6
|
import {
|
|
7
7
|
packageRoot,
|
|
8
8
|
logger
|
|
9
9
|
} from "./index.js";
|
|
10
10
|
import {
|
|
11
11
|
EJECTED_CONFIG_HEADER
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5m5nmvyq.js";
|
|
13
13
|
|
|
14
14
|
// src/cli/eject-scripts.ts
|
|
15
15
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -86,4 +86,4 @@ var refuseIfEjected = async (root, command) => {
|
|
|
86
86
|
export { updatePackageScripts, isEjectedProject, refuseIfEjected };
|
|
87
87
|
|
|
88
88
|
//# debugId=F07EB9DD3957B57764756E2164756E21
|
|
89
|
-
//# sourceMappingURL=chunk-
|
|
89
|
+
//# sourceMappingURL=chunk-ey84smr6.js.map
|
|
@@ -8,26 +8,26 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
commandsFor,
|
|
10
10
|
detectProjectPackageManager
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import"./chunk-
|
|
13
|
-
import"./chunk-
|
|
14
|
-
import"./chunk-
|
|
15
|
-
import"./chunk-
|
|
16
|
-
import"./chunk-
|
|
17
|
-
import"./chunk-
|
|
11
|
+
} from "./chunk-pv29h0wf.js";
|
|
12
|
+
import"./chunk-3em5wd2y.js";
|
|
13
|
+
import"./chunk-c8chx29p.js";
|
|
14
|
+
import"./chunk-yfyb25rh.js";
|
|
15
|
+
import"./chunk-j85vccga.js";
|
|
16
|
+
import"./chunk-5m5nmvyq.js";
|
|
17
|
+
import"./chunk-b07cmahc.js";
|
|
18
|
+
import"./chunk-8ktnccpt.js";
|
|
18
19
|
import"./chunk-jq5n4avg.js";
|
|
19
|
-
import"./chunk-
|
|
20
|
-
import"./chunk-fsmrqk8a.js";
|
|
20
|
+
import"./chunk-r20tn01b.js";
|
|
21
21
|
import {
|
|
22
22
|
eject
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-9t7a85s3.js";
|
|
24
24
|
import {
|
|
25
25
|
refuseIfDevRunning
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-akbpwfxc.js";
|
|
27
27
|
import {
|
|
28
28
|
updatePackageScripts,
|
|
29
29
|
isEjectedProject
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ey84smr6.js";
|
|
31
31
|
|
|
32
32
|
// src/cli/commands/eject.ts
|
|
33
33
|
import { defineCommand } from "citty";
|
|
@@ -93,4 +93,4 @@ export {
|
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
//# debugId=3D94285342361B2D64756E2164756E21
|
|
96
|
-
//# sourceMappingURL=chunk-
|
|
96
|
+
//# sourceMappingURL=chunk-g4hq16wv.js.map
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
var seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
5
5
|
var money = (cost) => cost === undefined ? "" : `$${cost.toFixed(2)}`;
|
|
6
6
|
var duration = (ms) => {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const tenths = Math.round(ms / 100);
|
|
8
|
+
if (tenths < 600) {
|
|
9
|
+
return `${(tenths / 10).toFixed(1)}s`;
|
|
9
10
|
}
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
return `${minutes}m ${rest}s`;
|
|
11
|
+
const total = Math.round(ms / 1000);
|
|
12
|
+
return `${Math.floor(total / 60)}m ${total % 60}s`;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
// src/eval/agents.ts
|
|
@@ -237,5 +237,5 @@ var parseVerdict = (text) => {
|
|
|
237
237
|
|
|
238
238
|
export { seconds, money, duration, DISALLOWED_TOOLS, runAgentHeadless, writeMcpConfig, CODEX_LOCKDOWN, agentArgs, readAgentOutput, parseVerdict };
|
|
239
239
|
|
|
240
|
-
//# debugId=
|
|
241
|
-
//# sourceMappingURL=chunk-
|
|
240
|
+
//# debugId=DE4C096172619DD564756E2164756E21
|
|
241
|
+
//# sourceMappingURL=chunk-ga0pf4aj.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/cli/report-format.ts", "../src/eval/agents.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Number formatting shared by the translate and eval report renderers. The\n * `1.5s` / `4m 12s` shapes are asserted in both suites — change them in both\n * minds at once.\n */\n\n/** Milliseconds as `1.5s`. */\nexport const seconds = (ms: number): string => `${(ms / 1000).toFixed(1)}s`;\n\n/** A dollar cost as `$1.23`, or nothing when the run reported none. */\nexport const money = (cost: number | undefined): string =>\n cost === undefined ? \"\" : `$${cost.toFixed(2)}`;\n\n
|
|
5
|
+
"/**\n * Number formatting shared by the translate and eval report renderers. The\n * `1.5s` / `4m 12s` shapes are asserted in both suites — change them in both\n * minds at once.\n */\n\n/** Milliseconds as `1.5s`. */\nexport const seconds = (ms: number): string => `${(ms / 1000).toFixed(1)}s`;\n\n/** A dollar cost as `$1.23`, or nothing when the run reported none. */\nexport const money = (cost: number | undefined): string =>\n cost === undefined ? \"\" : `$${cost.toFixed(2)}`;\n\n/**\n * Milliseconds as `12.3s` under a minute, `4m 12s` from there up. Rounding\n * happens before the split, so a value just under a boundary carries over\n * (`59_990` → `1m 0s`, `119_700` → `2m 0s`) instead of printing `60.0s` or\n * `1m 60s`.\n */\nexport const duration = (ms: number): string => {\n const tenths = Math.round(ms / 100);\n if (tenths < 600) {\n return `${(tenths / 10).toFixed(1)}s`;\n }\n const total = Math.round(ms / 1000);\n return `${Math.floor(total / 60)}m ${total % 60}s`;\n};\n",
|
|
6
6
|
"import { readFile, writeFile } from \"node:fs/promises\";\n\nimport spawn from \"cross-spawn\";\nimport { join } from \"pathe\";\nimport { z } from \"zod\";\n\nimport type { AgentKind } from \"../audit/agent.ts\";\n\n/** How long the SIGTERM on timeout gets to work before SIGKILL follows. */\nconst KILL_GRACE_MS = 5000;\n\n/** The MCP tools a reader run may use — nothing else. */\nexport const MCP_TOOL_NAMES = [\n \"search_docs\",\n \"get_page\",\n \"list_pages\",\n \"get_navigation\",\n] as const;\n\n/** The MCP server name in the generated config; tool ids derive from it. */\nconst MCP_SERVER_NAME = \"docs\";\n\n/**\n * Claude Code built-ins that would let the agent escape the docs-only\n * sandbox: the reader must not read the repo, run commands, or search the\n * web — it sees the documentation the way a stranger does, through MCP.\n */\nexport const DISALLOWED_TOOLS = [\n \"Bash\",\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"Write\",\n \"Edit\",\n \"NotebookEdit\",\n \"WebFetch\",\n \"WebSearch\",\n \"Task\",\n];\n\n/** The captured outcome of one headless agent invocation. */\nexport interface HeadlessResult {\n code: number;\n stderr: string;\n stdout: string;\n timedOut: boolean;\n}\n\nexport interface HeadlessOptions {\n cwd: string;\n /** How long the SIGTERM gets before SIGKILL follows (default 5s). */\n killGraceMs?: number;\n prompt: string;\n timeoutMs: number;\n}\n\n/**\n * Run an agent CLI headlessly: prompt over stdin (dodging argv limits and\n * cmd.exe newline quoting alike), stdout and stderr captured, SIGTERM at the\n * deadline with a SIGKILL follow-up. Resolves with the captured result;\n * rejects only when the executable cannot be spawned at all (ENOENT).\n *\n * Spawned through cross-spawn, which runs npm's `.cmd` shims on Windows with\n * its own cmd.exe escaping — under the old `shell: true` launch, cmd.exe was\n * free to mangle the JSON carried by codex's `-c` arguments, and a missing\n * executable surfaced as exit code 9009 instead of an ENOENT rejection.\n * execa was evaluated and rejected here: it waits for the stdio streams to\n * close, and the `exit`-beats-`close` resolution below exists precisely\n * because a killed agent's own children (an MCP server, a shell) hold the\n * pipes open past the SIGTERM.\n */\nexport const runAgentHeadless = (\n bin: string,\n args: string[],\n options: HeadlessOptions\n): Promise<HeadlessResult> =>\n // oxlint-disable-next-line promise/avoid-new -- adapt spawn's event callbacks\n new Promise((resolve, reject) => {\n const child = spawn(bin, args, {\n cwd: options.cwd,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString(\"utf-8\");\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf-8\");\n });\n\n let hardKill: ReturnType<typeof setTimeout> | undefined;\n const deadline = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGTERM\");\n hardKill = setTimeout(\n () => child.kill(\"SIGKILL\"),\n options.killGraceMs ?? KILL_GRACE_MS\n );\n hardKill.unref();\n }, options.timeoutMs);\n deadline.unref();\n\n child.once(\"error\", (error) => {\n clearTimeout(deadline);\n reject(error);\n });\n child.once(\"close\", (code) => {\n clearTimeout(deadline);\n resolve({ code: code ?? 1, stderr, stdout, timedOut });\n });\n // `close` waits for the stdio pipes, which a killed agent's own children\n // (an MCP server, a shell) can hold open past the SIGTERM. A timed-out\n // run's output is discarded anyway, so the process dying is enough.\n child.once(\"exit\", (code) => {\n // Once the process is gone the SIGKILL follow-up has nothing to do, and\n // firing it later could signal an unrelated process that reused the pid.\n clearTimeout(hardKill);\n if (timedOut) {\n clearTimeout(deadline);\n resolve({ code: code ?? 1, stderr, stdout, timedOut });\n }\n });\n\n // An agent that exits before reading the whole prompt (it rejected a flag,\n // say) closes the pipe mid-write, and the resulting EPIPE would otherwise\n // be an uncaught exception that aborts the run. The exit itself is the\n // outcome: `close` resolves with the agent's code and stderr, which\n // `readAgentOutput` reports.\n child.stdin?.on(\"error\", () => {\n // Deliberately ignored — see above.\n });\n child.stdin?.end(options.prompt);\n });\n\n/** The spawn signature `runEval` accepts, injectable for tests. */\nexport type HeadlessRunner = typeof runAgentHeadless;\n\n/** How the eval reaches the MCP stdio bridge from a spawned agent. */\nexport interface McpLaunch {\n /** The generated MCP config file (claude's `--mcp-config`). */\n configPath: string;\n /** argv for the bridge process (codex's `-c mcp_servers` override). */\n serverArgs: string[];\n /** The executable launching the bridge. */\n serverCommand: string;\n}\n\n/**\n * Write the MCP config a reader run points its agent CLI at. The bridge is\n * this same CLI relaunched (`blume mcp-stdio`), which resolves correctly from\n * both a source checkout (bun + src/cli/index.ts) and an installed package\n * (node + bin/blume.mjs).\n */\nexport const writeMcpConfig = async (\n dir: string,\n snapshotPath: string,\n launcher?: { args: string[]; command: string }\n): Promise<McpLaunch> => {\n const resolved = launcher ?? {\n args: [process.argv[1] ?? \"\", \"mcp-stdio\", \"--data\", snapshotPath],\n command: process.execPath,\n };\n const configPath = join(dir, \"mcp-config.json\");\n const config = {\n mcpServers: {\n [MCP_SERVER_NAME]: { args: resolved.args, command: resolved.command },\n },\n };\n await writeFile(configPath, JSON.stringify(config, null, 2));\n return {\n configPath,\n serverArgs: resolved.args,\n serverCommand: resolved.command,\n };\n};\n\nexport interface InvocationContext {\n /** Where codex writes its final message; unused by claude. */\n lastMessagePath: string;\n /** The MCP bridge for reader runs; omitted for the judge. */\n mcp?: McpLaunch;\n}\n\nconst CLAUDE_READER_MAX_TURNS = \"25\";\nconst CLAUDE_JUDGE_MAX_TURNS = \"1\";\n\nconst claudeArgs = (context: InvocationContext): string[] => {\n const base = [\"-p\", \"--output-format\", \"json\", \"--strict-mcp-config\"];\n if (context.mcp) {\n const allowed = MCP_TOOL_NAMES.map(\n (tool) => `mcp__${MCP_SERVER_NAME}__${tool}`\n ).join(\",\");\n return [\n ...base,\n \"--mcp-config\",\n context.mcp.configPath,\n \"--allowedTools\",\n allowed,\n \"--disallowedTools\",\n DISALLOWED_TOOLS.join(\",\"),\n \"--max-turns\",\n CLAUDE_READER_MAX_TURNS,\n ];\n }\n return [\n ...base,\n \"--disallowedTools\",\n DISALLOWED_TOOLS.join(\",\"),\n \"--max-turns\",\n CLAUDE_JUDGE_MAX_TURNS,\n ];\n};\n\n/**\n * Codex config overrides that leave an eval run nothing but its prompt and the\n * docs MCP tools (and a `blume translate` run nothing but its prompt). `--sandbox read-only` blocks writes and the network but still\n * lets the shell tool read any file the user can, and the docs a reader\n * searches — or the answer a judge grades — can come from a remote source, so\n * an instruction planted there could otherwise pull a local secret into the\n * transcript sent to the model provider. With both command tools and the\n * local-image tool off, and any subprocess started with no inherited\n * environment, there is nothing on the machine for a run to read.\n */\nexport const CODEX_LOCKDOWN = [\n \"-c\",\n \"features.shell_tool=false\",\n \"-c\",\n \"features.unified_exec=false\",\n \"-c\",\n \"tools.view_image=false\",\n \"-c\",\n 'shell_environment_policy.inherit=\"none\"',\n];\n\nconst codexArgs = (context: InvocationContext): string[] => {\n const base = [\n \"exec\",\n \"--skip-git-repo-check\",\n \"--ignore-user-config\",\n \"--ephemeral\",\n \"--sandbox\",\n \"read-only\",\n ...CODEX_LOCKDOWN,\n \"--output-last-message\",\n context.lastMessagePath,\n ];\n if (context.mcp) {\n // `-c` values parse as TOML; JSON string/array literals are valid TOML\n // values, so JSON.stringify produces exactly the quoting codex expects.\n return [\n ...base,\n \"-c\",\n `mcp_servers.${MCP_SERVER_NAME}.command=${JSON.stringify(\n context.mcp.serverCommand\n )}`,\n \"-c\",\n `mcp_servers.${MCP_SERVER_NAME}.args=${JSON.stringify(\n context.mcp.serverArgs\n )}`,\n \"-\",\n ];\n }\n return [...base, \"-\"];\n};\n\n/** Build the argv for one headless run; pass `mcp` for the reader role. */\nexport const agentArgs = (\n kind: AgentKind,\n context: InvocationContext\n): string[] => (kind === \"claude\" ? claudeArgs(context) : codexArgs(context));\n\n/** What one headless run produced, normalized across agent CLIs. */\nexport interface AgentOutput {\n costUsd?: number;\n detail?: string;\n isError: boolean;\n text: string;\n}\n\nconst claudeResultSchema = z.object({\n is_error: z.boolean().default(false),\n result: z.string().default(\"\"),\n total_cost_usd: z.number().optional(),\n});\n\nconst tail = (value: string, max = 300): string => {\n const trimmed = value.trim();\n return trimmed.length > max ? trimmed.slice(-max) : trimmed;\n};\n\n/**\n * Normalize a finished run into the answer text. Claude prints one JSON\n * object on stdout (`--output-format json`); codex writes the final message\n * to `--output-last-message` because its stdout interleaves progress.\n */\nexport const readAgentOutput = async (\n kind: AgentKind,\n result: HeadlessResult,\n lastMessagePath: string\n): Promise<AgentOutput> => {\n if (result.timedOut) {\n return { detail: \"timed out\", isError: true, text: \"\" };\n }\n if (result.code !== 0) {\n return {\n detail: tail(result.stderr) || `exited with code ${result.code}`,\n isError: true,\n text: \"\",\n };\n }\n\n if (kind === \"claude\") {\n let parsed: unknown;\n try {\n parsed = JSON.parse(result.stdout);\n } catch {\n return {\n detail: \"unparseable --output-format json payload\",\n isError: true,\n text: \"\",\n };\n }\n const payload = claudeResultSchema.safeParse(parsed);\n if (!payload.success) {\n return {\n detail: \"unexpected --output-format json shape\",\n isError: true,\n text: \"\",\n };\n }\n return {\n costUsd: payload.data.total_cost_usd,\n isError: payload.data.is_error,\n text: payload.data.result,\n };\n }\n\n let text: string;\n try {\n const raw = await readFile(lastMessagePath, \"utf-8\");\n text = raw.trim();\n } catch {\n return { detail: \"no last message written\", isError: true, text: \"\" };\n }\n if (text === \"\") {\n return { detail: \"empty last message\", isError: true, text: \"\" };\n }\n return { isError: false, text };\n};\n\nconst verdictSchema = z.object({\n missing: z.array(z.string()).default([]),\n notes: z.string().default(\"\"),\n pass: z.boolean(),\n score: z.number().min(0).max(1).optional(),\n});\n\n/** The judge's grade for one answer. */\nexport type Verdict = z.infer<typeof verdictSchema>;\n\n/**\n * Extract the verdict JSON from a judge reply. Tolerates markdown fences and\n * surrounding prose; returns undefined when no valid verdict can be found.\n */\nexport const parseVerdict = (text: string): Verdict | undefined => {\n const start = text.indexOf(\"{\");\n const end = text.lastIndexOf(\"}\");\n if (start === -1 || end <= start) {\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.slice(start, end + 1));\n } catch {\n return;\n }\n const result = verdictSchema.safeParse(parsed);\n return result.success ? result.data : undefined;\n};\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;AAOO,IAAM,UAAU,CAAC,OAAuB,IAAI,KAAK,MAAM,QAAQ,CAAC;AAGhE,IAAM,QAAQ,CAAC,SACpB,SAAS,YAAY,KAAK,IAAI,KAAK,QAAQ,CAAC;
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "DE4C096172619DD564756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -5,21 +5,20 @@ import {
|
|
|
5
5
|
BlumeError,
|
|
6
6
|
countBySeverity,
|
|
7
7
|
logger,
|
|
8
|
-
flushStdout,
|
|
9
8
|
reportDiagnostics
|
|
10
9
|
} from "./index.js";
|
|
11
10
|
import {
|
|
12
11
|
AGENTS
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-1d7ve1dm.js";
|
|
14
13
|
import {
|
|
15
|
-
frontmatter_default,
|
|
16
14
|
i18nEnabled,
|
|
17
15
|
localeCodes,
|
|
18
16
|
localeTargetPath,
|
|
17
|
+
frontmatter_default,
|
|
19
18
|
folderMetaSchema,
|
|
20
19
|
scanBody,
|
|
21
20
|
createModuleLoader
|
|
22
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-pv29h0wf.js";
|
|
23
22
|
import {
|
|
24
23
|
seconds,
|
|
25
24
|
money,
|
|
@@ -28,14 +27,18 @@ import {
|
|
|
28
27
|
runAgentHeadless,
|
|
29
28
|
CODEX_LOCKDOWN,
|
|
30
29
|
readAgentOutput
|
|
31
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ga0pf4aj.js";
|
|
32
31
|
import {
|
|
33
32
|
writeTextAtomic
|
|
34
33
|
} from "./chunk-jq5n4avg.js";
|
|
35
|
-
import"./chunk-gs7r695n.js";
|
|
36
34
|
import {
|
|
35
|
+
findFolderMetaFiles,
|
|
37
36
|
scanProject
|
|
38
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-j85vccga.js";
|
|
38
|
+
import"./chunk-3em5wd2y.js";
|
|
39
|
+
import {
|
|
40
|
+
parseTimeoutSeconds
|
|
41
|
+
} from "./chunk-e04dxsz1.js";
|
|
39
42
|
|
|
40
43
|
// src/cli/commands/translate.ts
|
|
41
44
|
import { defineCommand } from "citty";
|
|
@@ -355,7 +358,7 @@ var createProgressRenderer = (options) => {
|
|
|
355
358
|
};
|
|
356
359
|
|
|
357
360
|
// src/translate/run.ts
|
|
358
|
-
import { existsSync } from "node:fs";
|
|
361
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
359
362
|
import { mkdtemp, readFile as readFile3 } from "node:fs/promises";
|
|
360
363
|
import { tmpdir } from "node:os";
|
|
361
364
|
import pLimit from "p-limit";
|
|
@@ -363,12 +366,17 @@ import pMap from "p-map";
|
|
|
363
366
|
import { dirname as dirname2, join as join3 } from "pathe";
|
|
364
367
|
|
|
365
368
|
// src/translate/meta.ts
|
|
369
|
+
import { existsSync } from "node:fs";
|
|
366
370
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
367
371
|
import { dirname, join as join2, relative } from "pathe";
|
|
368
|
-
|
|
369
|
-
var META_FILES =
|
|
372
|
+
var META_NAMES = ["meta.ts", "meta.js", "meta.mjs"];
|
|
373
|
+
var META_FILES = META_NAMES.map((name) => `**/${name}`);
|
|
370
374
|
var isFactoryModule = (value) => typeof value === "function";
|
|
371
|
-
var metaTargetPath = (meta, locale
|
|
375
|
+
var metaTargetPath = (meta, locale, i18n, folders) => {
|
|
376
|
+
const dir = join2(meta.contentRoot, dirname(localeTargetPath(join2(meta.dir, "meta.ts"), ".ts", locale, i18n, folders)));
|
|
377
|
+
const existing = META_NAMES.find((name) => existsSync(join2(dir, name)));
|
|
378
|
+
return join2(dir, existing ?? "meta.ts");
|
|
379
|
+
};
|
|
372
380
|
var discoverTranslatableMeta = async (project) => {
|
|
373
381
|
const { i18n } = project.config;
|
|
374
382
|
if (!i18n || i18n.parser !== "dir") {
|
|
@@ -379,24 +387,28 @@ var discoverTranslatableMeta = async (project) => {
|
|
|
379
387
|
const load = createModuleLoader();
|
|
380
388
|
const metas = [];
|
|
381
389
|
const diagnostics = [];
|
|
382
|
-
const roots = project.sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
const roots = project.sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [
|
|
391
|
+
{
|
|
392
|
+
contentRoot: source.contentRoot,
|
|
393
|
+
exclude: source.exclude,
|
|
394
|
+
include: source.include
|
|
395
|
+
}
|
|
396
|
+
]);
|
|
397
|
+
for (const { contentRoot, exclude, include } of roots) {
|
|
398
|
+
const files = await findFolderMetaFiles({ exclude, include, root: contentRoot }, META_FILES);
|
|
390
399
|
for (const file of files.toSorted()) {
|
|
391
400
|
const dir = relative(contentRoot, dirname(file));
|
|
392
401
|
const [head] = dir.split("/");
|
|
393
402
|
if (head && (localeDirs.has(head.toLowerCase()) || versionDirs.has(head))) {
|
|
394
403
|
continue;
|
|
395
404
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
load(file)
|
|
399
|
-
|
|
405
|
+
let mod;
|
|
406
|
+
try {
|
|
407
|
+
mod = await load(file);
|
|
408
|
+
} catch {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const raw = await readFile2(file, "utf-8");
|
|
400
412
|
if (isFactoryModule(mod)) {
|
|
401
413
|
diagnostics.push({
|
|
402
414
|
code: "BLUME_TRANSLATE_META_FACTORY",
|
|
@@ -533,7 +545,7 @@ var pinHeadingAnchors = (sourceText, translatedText) => {
|
|
|
533
545
|
};
|
|
534
546
|
|
|
535
547
|
// src/translate/validate.ts
|
|
536
|
-
var FRONTMATTER_OPEN =
|
|
548
|
+
var FRONTMATTER_OPEN = /^?---\r?\n/u;
|
|
537
549
|
var FENCE_LINE = /^\s*(?:```|~~~)/u;
|
|
538
550
|
var stripOuterFence = (text) => {
|
|
539
551
|
const trimmed = text.trim();
|
|
@@ -661,7 +673,7 @@ var runPageItem = async (item, index, context, ledger) => {
|
|
|
661
673
|
return done("translated");
|
|
662
674
|
}
|
|
663
675
|
const target = context.targets.get(item.locale);
|
|
664
|
-
const previousTranslation =
|
|
676
|
+
const previousTranslation = existsSync2(item.targetPath) ? await readFile3(item.targetPath, "utf-8") : undefined;
|
|
665
677
|
const output = await invokeAgent(context, pagePrompt(sourceText, target, context.source, previousTranslation), index);
|
|
666
678
|
if (output.isError) {
|
|
667
679
|
return done("failed", output.detail ?? "agent failed", output.costUsd);
|
|
@@ -782,9 +794,9 @@ var runTranslate = async (options) => {
|
|
|
782
794
|
};
|
|
783
795
|
|
|
784
796
|
// src/translate/work-list.ts
|
|
785
|
-
import { existsSync as
|
|
797
|
+
import { existsSync as existsSync3, readdirSync } from "node:fs";
|
|
786
798
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
787
|
-
import {
|
|
799
|
+
import { extname, join as join4, relative as relative2 } from "pathe";
|
|
788
800
|
var PAGE_EXTENSIONS = new Set([".md", ".mdx"]);
|
|
789
801
|
var rootFolders = () => {
|
|
790
802
|
const read = new Map;
|
|
@@ -873,7 +885,7 @@ var partialWorkItems = async (universe, project, i18n, ledger, targetLocales, fo
|
|
|
873
885
|
return partialItem;
|
|
874
886
|
};
|
|
875
887
|
const stamp = ledger.files[sourceRel]?.[locale];
|
|
876
|
-
const outcome = classifyPair(
|
|
888
|
+
const outcome = classifyPair(existsSync3(targetPath), stamp, hash, force);
|
|
877
889
|
if (outcome === "untracked") {
|
|
878
890
|
work.untracked.push({ hash, kind: "page", locale, sourceRel });
|
|
879
891
|
} else if (outcome === "current") {
|
|
@@ -885,6 +897,18 @@ var partialWorkItems = async (universe, project, i18n, ledger, targetLocales, fo
|
|
|
885
897
|
}
|
|
886
898
|
return work;
|
|
887
899
|
};
|
|
900
|
+
var existingTranslations = (project) => {
|
|
901
|
+
const writableSources = new Set(project.sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [source.name]));
|
|
902
|
+
const translated = new Map;
|
|
903
|
+
for (const page of project.graph.pages) {
|
|
904
|
+
const key = `${page.translationKey}\x00${page.locale}`;
|
|
905
|
+
if (page.fallback || translated.has(key)) {
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
translated.set(key, page.sourcePath && writableSources.has(page.source.name) ? page.sourcePath : undefined);
|
|
909
|
+
}
|
|
910
|
+
return translated;
|
|
911
|
+
};
|
|
888
912
|
var computeWorkList = async (project, ledger, options = {}) => {
|
|
889
913
|
const { i18n } = project.config;
|
|
890
914
|
if (!i18n) {
|
|
@@ -898,7 +922,7 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
898
922
|
};
|
|
899
923
|
}
|
|
900
924
|
const targetLocales = localeCodes(i18n).filter((code) => code !== i18n.defaultLocale && (options.locales === undefined || options.locales.includes(code)));
|
|
901
|
-
const translated =
|
|
925
|
+
const translated = existingTranslations(project);
|
|
902
926
|
const { root } = project.context;
|
|
903
927
|
const knownSources = new Set;
|
|
904
928
|
const untracked = [];
|
|
@@ -912,7 +936,8 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
912
936
|
const hash = hashSource(await readFile4(sourcePath, "utf-8"));
|
|
913
937
|
const contentRel = relative2(contentRoot, sourcePath);
|
|
914
938
|
for (const locale of targetLocales) {
|
|
915
|
-
const
|
|
939
|
+
const key = `${page.translationKey}\x00${locale}`;
|
|
940
|
+
const targetPath = translated.get(key) ?? join4(contentRoot, localeTargetPath(contentRel, ext, locale, i18n, folders(contentRoot)));
|
|
916
941
|
const item = (status) => ({
|
|
917
942
|
kind: "page",
|
|
918
943
|
locale,
|
|
@@ -922,7 +947,7 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
922
947
|
targetPath,
|
|
923
948
|
targetRel: relative2(root, targetPath)
|
|
924
949
|
});
|
|
925
|
-
const exists = translated.has(
|
|
950
|
+
const exists = translated.has(key);
|
|
926
951
|
const stamp = ledger.files[sourceRel]?.[locale];
|
|
927
952
|
const outcome = classifyPair(exists, stamp, hash, options.force === true);
|
|
928
953
|
if (outcome === "untracked") {
|
|
@@ -947,14 +972,14 @@ var computeWorkList = async (project, ledger, options = {}) => {
|
|
|
947
972
|
const entries = [];
|
|
948
973
|
for (const source of meta.metas) {
|
|
949
974
|
knownSources.add(source.sourceRel);
|
|
950
|
-
const
|
|
951
|
-
const exists =
|
|
975
|
+
const targetPath = metaTargetPath(source, locale, i18n, folders(source.contentRoot));
|
|
976
|
+
const exists = existsSync3(targetPath);
|
|
952
977
|
const hash = hashSource(source.raw);
|
|
953
978
|
const stamp = ledger.files[source.sourceRel]?.[locale];
|
|
954
979
|
const entry = (status) => ({
|
|
955
980
|
meta: source,
|
|
956
981
|
status,
|
|
957
|
-
targetPath
|
|
982
|
+
targetPath
|
|
958
983
|
});
|
|
959
984
|
const outcome = classifyPair(exists, stamp, hash, options.force === true);
|
|
960
985
|
if (outcome === "untracked") {
|
|
@@ -1004,11 +1029,7 @@ var parseFlags = (args) => {
|
|
|
1004
1029
|
logger.error("Pass --codex or --claude to choose the agent CLI that translates.");
|
|
1005
1030
|
process.exit(1);
|
|
1006
1031
|
}
|
|
1007
|
-
const timeoutS = args.timeout
|
|
1008
|
-
if (!Number.isInteger(timeoutS) || timeoutS <= 0) {
|
|
1009
|
-
logger.error(`Invalid --timeout "${args.timeout}" (whole seconds).`);
|
|
1010
|
-
process.exit(1);
|
|
1011
|
-
}
|
|
1032
|
+
const timeoutS = parseTimeoutSeconds(args.timeout, DEFAULT_TIMEOUT_S);
|
|
1012
1033
|
const concurrency = args.concurrency === undefined ? DEFAULT_CONCURRENCY : Number(args.concurrency);
|
|
1013
1034
|
if (!Number.isInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {
|
|
1014
1035
|
logger.error(`Invalid --concurrency "${args.concurrency}" (use 1..${MAX_CONCURRENCY}).`);
|
|
@@ -1112,8 +1133,7 @@ var translateCommand = defineCommand({
|
|
|
1112
1133
|
process.stdout.write(checkReportJson(workList));
|
|
1113
1134
|
}
|
|
1114
1135
|
if (hasDrift(workList)) {
|
|
1115
|
-
|
|
1116
|
-
process.exit(1);
|
|
1136
|
+
process.exitCode = 1;
|
|
1117
1137
|
}
|
|
1118
1138
|
return;
|
|
1119
1139
|
}
|
|
@@ -1154,8 +1174,7 @@ var translateCommand = defineCommand({
|
|
|
1154
1174
|
process.stdout.write(translateReportJson(result, workList));
|
|
1155
1175
|
}
|
|
1156
1176
|
if (result.counts.failed > 0 || result.counts.partial > 0) {
|
|
1157
|
-
|
|
1158
|
-
process.exit(1);
|
|
1177
|
+
process.exitCode = 1;
|
|
1159
1178
|
}
|
|
1160
1179
|
} catch (error) {
|
|
1161
1180
|
if (error instanceof BlumeError) {
|
|
@@ -1174,5 +1193,5 @@ export {
|
|
|
1174
1193
|
translateCommand
|
|
1175
1194
|
};
|
|
1176
1195
|
|
|
1177
|
-
//# debugId=
|
|
1178
|
-
//# sourceMappingURL=chunk-
|
|
1196
|
+
//# debugId=FFF735A581A85E1364756E2164756E21
|
|
1197
|
+
//# sourceMappingURL=chunk-hm3vjy5s.js.map
|