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.
- package/CHANGELOG.md +21 -0
- package/dist/cli/index.js +1318 -270
- package/dist/cli/index.js.map +68 -61
- package/dist/types/core/config-input.d.ts +9 -0
- package/dist/types/core/data.d.ts +12 -1
- package/dist/types/core/i18n-ui.d.ts +4 -0
- package/dist/types/core/schema.d.ts +7 -0
- package/dist/types/core/types.d.ts +6 -0
- package/dist/types/openapi/references.d.ts +5 -0
- package/docs/07-faq.mdx +9 -9
- package/docs/advanced/api-reference.mdx +10 -1
- package/docs/advanced/custom-pages.mdx +3 -1
- package/docs/advanced/graphql.mdx +1 -1
- package/docs/configuration/ai.mdx +76 -7
- package/docs/configuration/seo.mdx +3 -3
- package/docs/configuration/theming.mdx +6 -0
- package/docs/content/components.mdx +8 -1
- package/docs/index.mdx +2 -2
- package/package.json +53 -53
- package/skills/blume/SKILL.md +2 -2
- package/src/ai/agent-readability.ts +60 -17
- package/src/ai/api/handlers.ts +273 -0
- package/src/ai/api/paths.ts +14 -0
- package/src/ai/api/problem.ts +63 -0
- package/src/ai/api/spec.ts +681 -0
- package/src/ai/api-catalog.ts +11 -1
- package/src/ai/link-headers.ts +12 -3
- package/src/ai/llms.ts +9 -2
- package/src/ai/mcp/query.ts +390 -0
- package/src/ai/mcp/server.ts +32 -352
- package/src/astro/examples.ts +29 -2
- package/src/astro/generate.ts +256 -64
- package/src/astro/index.ts +7 -0
- package/src/astro/markdown-negotiation.ts +1 -1
- package/src/astro/runtime-modules.ts +196 -0
- package/src/astro/templates.ts +398 -38
- package/src/cli/commands/build.ts +9 -1
- package/src/cli/commands/dev.ts +6 -3
- package/src/cli/host-args.ts +18 -0
- package/src/cli/index.ts +2 -1
- package/src/components/copy-feedback.ts +93 -9
- package/src/components/islands/ask-ai.tsx +4 -1
- package/src/components/islands/hooks.ts +3 -1
- package/src/components/layout/PageActions.astro +25 -14
- package/src/core/config-input.ts +9 -0
- package/src/core/data.ts +17 -2
- package/src/core/define-components.ts +2 -0
- package/src/core/i18n-ui.ts +3 -0
- package/src/core/includes.ts +2 -1
- package/src/core/manifest.ts +10 -0
- package/src/core/schema.ts +20 -5
- package/src/core/types.ts +6 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +1 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/core/version-cut.ts +5 -3
- package/src/deploy/vercel-negotiation.ts +97 -6
- package/src/og/card.ts +1 -1
- package/src/openapi/references.ts +8 -0
- package/src/openapi/render-mdx.ts +18 -4
- package/src/openapi/scalar.ts +0 -4
- package/src/registry/eject.ts +36 -17
- package/src/theme/entry.ts +2 -2
- 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
|
+
});
|