blume 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cli/index.js +1318 -270
  3. package/dist/cli/index.js.map +68 -61
  4. package/dist/types/core/config-input.d.ts +9 -0
  5. package/dist/types/core/data.d.ts +12 -1
  6. package/dist/types/core/i18n-ui.d.ts +4 -0
  7. package/dist/types/core/schema.d.ts +7 -0
  8. package/dist/types/core/types.d.ts +6 -0
  9. package/dist/types/openapi/references.d.ts +5 -0
  10. package/docs/07-faq.mdx +9 -9
  11. package/docs/advanced/api-reference.mdx +10 -1
  12. package/docs/advanced/custom-pages.mdx +3 -1
  13. package/docs/advanced/graphql.mdx +1 -1
  14. package/docs/configuration/ai.mdx +76 -7
  15. package/docs/configuration/seo.mdx +3 -3
  16. package/docs/configuration/theming.mdx +6 -0
  17. package/docs/content/components.mdx +8 -1
  18. package/docs/index.mdx +2 -2
  19. package/package.json +53 -53
  20. package/skills/blume/SKILL.md +2 -2
  21. package/src/ai/agent-readability.ts +60 -17
  22. package/src/ai/api/handlers.ts +273 -0
  23. package/src/ai/api/paths.ts +14 -0
  24. package/src/ai/api/problem.ts +63 -0
  25. package/src/ai/api/spec.ts +681 -0
  26. package/src/ai/api-catalog.ts +11 -1
  27. package/src/ai/link-headers.ts +12 -3
  28. package/src/ai/llms.ts +9 -2
  29. package/src/ai/mcp/query.ts +390 -0
  30. package/src/ai/mcp/server.ts +32 -352
  31. package/src/astro/examples.ts +29 -2
  32. package/src/astro/generate.ts +256 -64
  33. package/src/astro/index.ts +7 -0
  34. package/src/astro/markdown-negotiation.ts +1 -1
  35. package/src/astro/runtime-modules.ts +196 -0
  36. package/src/astro/templates.ts +398 -38
  37. package/src/cli/commands/build.ts +9 -1
  38. package/src/cli/commands/dev.ts +6 -3
  39. package/src/cli/host-args.ts +18 -0
  40. package/src/cli/index.ts +2 -1
  41. package/src/components/copy-feedback.ts +93 -9
  42. package/src/components/islands/ask-ai.tsx +4 -1
  43. package/src/components/islands/hooks.ts +3 -1
  44. package/src/components/layout/PageActions.astro +25 -14
  45. package/src/core/config-input.ts +9 -0
  46. package/src/core/data.ts +17 -2
  47. package/src/core/define-components.ts +2 -0
  48. package/src/core/i18n-ui.ts +3 -0
  49. package/src/core/includes.ts +2 -1
  50. package/src/core/manifest.ts +10 -0
  51. package/src/core/schema.ts +20 -5
  52. package/src/core/types.ts +6 -0
  53. package/src/core/ui-packs/ar.ts +1 -0
  54. package/src/core/ui-packs/bg.ts +1 -0
  55. package/src/core/ui-packs/bn.ts +1 -0
  56. package/src/core/ui-packs/ca.ts +1 -0
  57. package/src/core/ui-packs/cs.ts +1 -0
  58. package/src/core/ui-packs/da.ts +1 -0
  59. package/src/core/ui-packs/de.ts +1 -0
  60. package/src/core/ui-packs/el.ts +1 -0
  61. package/src/core/ui-packs/es.ts +1 -0
  62. package/src/core/ui-packs/fa.ts +1 -0
  63. package/src/core/ui-packs/fi.ts +1 -0
  64. package/src/core/ui-packs/fr.ts +1 -0
  65. package/src/core/ui-packs/he.ts +1 -0
  66. package/src/core/ui-packs/hi.ts +1 -0
  67. package/src/core/ui-packs/hr.ts +1 -0
  68. package/src/core/ui-packs/hu.ts +1 -0
  69. package/src/core/ui-packs/id.ts +1 -0
  70. package/src/core/ui-packs/it.ts +1 -0
  71. package/src/core/ui-packs/ja.ts +1 -0
  72. package/src/core/ui-packs/ko.ts +1 -0
  73. package/src/core/ui-packs/nl.ts +1 -0
  74. package/src/core/ui-packs/no.ts +1 -0
  75. package/src/core/ui-packs/pl.ts +1 -0
  76. package/src/core/ui-packs/pt-br.ts +1 -0
  77. package/src/core/ui-packs/pt.ts +1 -0
  78. package/src/core/ui-packs/ro.ts +1 -0
  79. package/src/core/ui-packs/ru.ts +1 -0
  80. package/src/core/ui-packs/sk.ts +1 -0
  81. package/src/core/ui-packs/sr.ts +1 -0
  82. package/src/core/ui-packs/sv.ts +1 -0
  83. package/src/core/ui-packs/th.ts +1 -0
  84. package/src/core/ui-packs/tr.ts +1 -0
  85. package/src/core/ui-packs/uk.ts +1 -0
  86. package/src/core/ui-packs/vi.ts +1 -0
  87. package/src/core/ui-packs/zh-tw.ts +1 -0
  88. package/src/core/ui-packs/zh.ts +1 -0
  89. package/src/core/version-cut.ts +5 -3
  90. package/src/deploy/vercel-negotiation.ts +97 -6
  91. package/src/og/card.ts +1 -1
  92. package/src/openapi/references.ts +8 -0
  93. package/src/openapi/render-mdx.ts +18 -4
  94. package/src/openapi/scalar.ts +0 -4
  95. package/src/registry/eject.ts +36 -17
  96. package/src/theme/entry.ts +2 -2
  97. package/src/theme/sources.ts +49 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * In-memory runtime data modules.
3
+ *
4
+ * The generated runtime's data snapshots — the page data behind `blume:data`,
5
+ * the parsed API specs, the static search index, the raw-Markdown and
6
+ * content-asset maps, the MCP and Ask corpora, and the rendered RSS feeds —
7
+ * used to be written under `.blume/src/generated/*.json` and imported by the
8
+ * generated pages through aliases or relative paths. `generateRuntime` now
9
+ * publishes them here and `runtimeModulesPlugin` serves them to Vite as
10
+ * virtual modules, so a regeneration never round-trips the disk or waits on a
11
+ * file watcher: publishing invalidates the changed modules in every live dev
12
+ * server (Vite walks the importers, so the pages that render them re-evaluate
13
+ * on the next request) and asks the browser to reload — the same effect a JSON
14
+ * file change used to reach through the watcher, minus the write and the
15
+ * watch debounce.
16
+ *
17
+ * The registry hangs off `globalThis`, not module state. On a published
18
+ * install the CLI bundle (`dist/cli`) carries its own copy of this module,
19
+ * separate from the one Vite loads from `blume/astro` for the generated
20
+ * config, and both must see the same map. `blume dev`, `blume build`, and
21
+ * `blume check` all run Astro in-process, so the map the CLI fills is the map
22
+ * the plugin reads.
23
+ *
24
+ * `blume eject` keeps the file form: the ejected project has no CLI to
25
+ * publish, so its `astro.config.mjs` aliases each id to the JSON file eject
26
+ * writes under `src/generated/` — the names in {@link RUNTIME_MODULE_FILES}.
27
+ */
28
+
29
+ export type RuntimeModuleId =
30
+ | "blume:ask-data"
31
+ | "blume:content-assets"
32
+ | "blume:data"
33
+ | "blume:mcp-data"
34
+ | "blume:openapi"
35
+ | "blume:raw-markdown"
36
+ | "blume:rss"
37
+ | "blume:search-index";
38
+
39
+ /** Virtual module id → the `src/generated` JSON file eject writes for it. */
40
+ export const RUNTIME_MODULE_FILES: ReadonlyMap<RuntimeModuleId, string> =
41
+ new Map([
42
+ ["blume:ask-data", "ask-data.json"],
43
+ ["blume:content-assets", "content-assets.json"],
44
+ ["blume:data", "data.json"],
45
+ ["blume:mcp-data", "mcp-data.json"],
46
+ ["blume:openapi", "openapi.json"],
47
+ ["blume:raw-markdown", "raw-markdown.json"],
48
+ ["blume:rss", "rss.json"],
49
+ ["blume:search-index", "search.json"],
50
+ ]);
51
+
52
+ const RUNTIME_MODULE_IDS: ReadonlySet<string> = new Set(
53
+ RUNTIME_MODULE_FILES.keys()
54
+ );
55
+
56
+ /** Rollup's virtual-module convention: `\0` keeps other plugins off the id. */
57
+ const RESOLVED_PREFIX = "\0";
58
+
59
+ /** A node in Vite's module graph; only its identity matters here. */
60
+ interface RuntimeModuleNode {
61
+ id: string | null;
62
+ }
63
+
64
+ /**
65
+ * The Vite dev-server slice the registry touches (structurally typed, like
66
+ * every Blume-authored Vite plugin — see `includeHmrPlugin`).
67
+ */
68
+ export interface RuntimeModuleServer {
69
+ httpServer?: {
70
+ once: (event: "close", listener: () => void) => void;
71
+ } | null;
72
+ moduleGraph: {
73
+ getModuleById: (id: string) => RuntimeModuleNode | undefined;
74
+ invalidateModule: (mod: RuntimeModuleNode) => void;
75
+ };
76
+ ws: { send: (payload: { type: "full-reload" }) => void };
77
+ }
78
+
79
+ interface RuntimeModuleRegistry {
80
+ /** Published JSON text by module id. */
81
+ modules: Map<string, string>;
82
+ /** Live dev servers to invalidate on publish. */
83
+ servers: Set<RuntimeModuleServer>;
84
+ }
85
+
86
+ const REGISTRY_KEY = Symbol.for("blume.runtime-modules");
87
+
88
+ type RegistryHost = typeof globalThis & {
89
+ [REGISTRY_KEY]?: RuntimeModuleRegistry;
90
+ };
91
+
92
+ const registry = (): RuntimeModuleRegistry => {
93
+ // SAFETY: the registry is stashed on globalThis under a well-known symbol so
94
+ // every copy of this module in the process shares it; the intersection only
95
+ // names that slot.
96
+ const host = globalThis as RegistryHost;
97
+ host[REGISTRY_KEY] ??= { modules: new Map(), servers: new Set() };
98
+ return host[REGISTRY_KEY];
99
+ };
100
+
101
+ /** The published JSON text for a module, if any (tests and diagnostics). */
102
+ export const readRuntimeModule = (id: RuntimeModuleId): string | undefined =>
103
+ registry().modules.get(id);
104
+
105
+ /**
106
+ * Replace the published snapshot set with `modules` (an id absent from the map
107
+ * is unpublished — its feature was switched off). Returns the ids whose text
108
+ * changed; each is invalidated in every live dev server, followed by one
109
+ * full-reload per server. Nothing is sent when nothing changed, so a
110
+ * regeneration triggered by an unrelated edit stays quiet — the same contract
111
+ * `writeIfChanged` gave the file form.
112
+ */
113
+ export const publishRuntimeModules = (
114
+ modules: ReadonlyMap<RuntimeModuleId, string>
115
+ ): RuntimeModuleId[] => {
116
+ const { modules: current, servers } = registry();
117
+ const changed: RuntimeModuleId[] = [];
118
+ for (const id of RUNTIME_MODULE_FILES.keys()) {
119
+ const next = modules.get(id);
120
+ if (current.get(id) === next) {
121
+ continue;
122
+ }
123
+ if (next === undefined) {
124
+ current.delete(id);
125
+ } else {
126
+ current.set(id, next);
127
+ }
128
+ changed.push(id);
129
+ }
130
+ if (changed.length === 0) {
131
+ return changed;
132
+ }
133
+ for (const server of servers) {
134
+ for (const id of changed) {
135
+ const mod = server.moduleGraph.getModuleById(`${RESOLVED_PREFIX}${id}`);
136
+ if (mod) {
137
+ server.moduleGraph.invalidateModule(mod);
138
+ }
139
+ }
140
+ server.ws.send({ type: "full-reload" });
141
+ }
142
+ return changed;
143
+ };
144
+
145
+ export interface RuntimeModulesPlugin {
146
+ configureServer: (server: RuntimeModuleServer) => void;
147
+ enforce: "pre";
148
+ load: (id: string) => string | undefined;
149
+ name: string;
150
+ resolveId: (id: string) => string | undefined;
151
+ }
152
+
153
+ /**
154
+ * Serve the published runtime modules to Vite. `enforce: "pre"` claims the
155
+ * `blume:*` ids before Vite's resolver would try (and fail) to find them as
156
+ * packages. Loading an unpublished id is a hard error rather than an empty
157
+ * module: the generated pages only import a module when its data exists, so a
158
+ * miss means the config was run outside the CLI that publishes.
159
+ */
160
+ export const runtimeModulesPlugin = (): RuntimeModulesPlugin => ({
161
+ configureServer(server) {
162
+ const { servers } = registry();
163
+ servers.add(server);
164
+ // Astro restarts the dev container in place on a config change (and the
165
+ // CLI restarts it on a route-set change): the old Vite server closes and
166
+ // a new one registers, so drop the stale handle rather than invalidating
167
+ // into a dead graph.
168
+ server.httpServer?.once("close", () => {
169
+ servers.delete(server);
170
+ });
171
+ },
172
+ enforce: "pre",
173
+ load(id) {
174
+ if (!id.startsWith(RESOLVED_PREFIX)) {
175
+ return;
176
+ }
177
+ const moduleId = id.slice(RESOLVED_PREFIX.length);
178
+ if (!RUNTIME_MODULE_IDS.has(moduleId)) {
179
+ return;
180
+ }
181
+ const text = registry().modules.get(moduleId);
182
+ if (text === undefined) {
183
+ throw new Error(
184
+ `Blume runtime module "${moduleId}" was requested before it was published. Run the generated project through the Blume CLI (blume dev / blume build), which publishes the runtime data before starting Astro.`
185
+ );
186
+ }
187
+ // A JSON.parse over a string literal evaluates faster than an equivalent
188
+ // object literal for large snapshots (V8's guidance; Vite's own JSON
189
+ // plugin does the same past a size threshold).
190
+ return `export default JSON.parse(${JSON.stringify(text)});\n`;
191
+ },
192
+ name: "blume:runtime-modules",
193
+ resolveId(id) {
194
+ return RUNTIME_MODULE_IDS.has(id) ? `${RESOLVED_PREFIX}${id}` : undefined;
195
+ },
196
+ });