@kurajs/cli 0.0.2 → 0.0.4

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 (2) hide show
  1. package/dist/cli.js +42 -23
  2. package/package.json +9 -5
package/dist/cli.js CHANGED
@@ -6,7 +6,6 @@
6
6
  // and never reads the filesystem (Workers-safe; mirrors June's app/_content.ts freeze).
7
7
  // A content hash short-circuits re-embedding when nothing changed (cheap to run pre-dev).
8
8
  import { buildIndex } from "@kurajs/docs/search";
9
- import { transformers } from "@kurajs/transformers";
10
9
  import fs from "node:fs";
11
10
  import path from "node:path";
12
11
  import crypto from "node:crypto";
@@ -53,28 +52,45 @@ async function cmdIndex() {
53
52
  if (e.locale === locale)
54
53
  variants.push(e);
55
54
  const allEntries = [...DOCS, ...variants];
56
- // Content hash — skip the (slow) embed when nothing changed, so `kura index` is cheap to run
57
- // before every dev/build. Covers locale + slug + body of every entry, plus the model id.
58
- const hashInput = JSON.stringify([model, allEntries.map((e) => [e.locale ?? "", e.slug, e.body])]);
55
+ // --no-embed: skip the search index entirely (build MDX only). Use it when the site runs
56
+ // WITHOUT a runtime embedder search degrades to a lexical scan, so no model/index is needed
57
+ // (e.g. a Cloudflare Workers deploy without Workers AI).
58
+ const noEmbed = process.argv.includes("--no-embed");
59
+ // Content hash — skip rebuilds when nothing changed, so `kura index` is cheap to run before
60
+ // every dev/build. Covers the mode + model + locale/slug/body of every entry.
61
+ const hashInput = JSON.stringify([model, noEmbed, allEntries.map((e) => [e.locale ?? "", e.slug, e.body])]);
59
62
  const contentHash = crypto.createHash("sha256").update(hashInput).digest("hex").slice(0, 16);
60
- if (fs.existsSync(mdxTs) && fs.existsSync(indexTs)) {
61
- const prev = fs.readFileSync(indexTs, "utf8").match(/CONTENT_HASH = "([^"]+)"/)?.[1];
62
- if (prev === contentHash) {
63
- console.log(`kura index: up to date (${allEntries.length} docs, hash ${contentHash}) — skipped`);
64
- return;
65
- }
63
+ const stamp = `// content-hash: ${contentHash}\n`;
64
+ const hashOf = (f) => (fs.existsSync(f) ? fs.readFileSync(f, "utf8").match(/content-hash: (\S+)/)?.[1] : undefined);
65
+ const upToDate = noEmbed ? hashOf(mdxTs) === contentHash : hashOf(indexTs) === contentHash && fs.existsSync(mdxTs);
66
+ if (upToDate) {
67
+ console.log(`kura index: up to date (${allEntries.length} docs, hash ${contentHash}) — skipped`);
68
+ return;
69
+ }
70
+ fs.mkdirSync(path.dirname(mdxTs), { recursive: true });
71
+ if (!noEmbed) {
72
+ const localeTag = locales.size ? ` (+${variants.length} variants across ${locales.size} locales)` : "";
73
+ console.log(`kura index: embedding ${DOCS.length} docs${localeTag} (model ${model})…`);
74
+ const t0 = Date.now();
75
+ // Lazy import: only the embed path needs the ML stack, so --no-embed sites never load it
76
+ // (and never install @kurajs/transformers — it's an optional peer).
77
+ const { transformers } = await import("@kurajs/transformers").catch(() => {
78
+ console.error("kura index: embedding needs @kurajs/transformers — install it, or use --no-embed.");
79
+ return process.exit(1);
80
+ });
81
+ const bytes = await buildIndex({ entries: allEntries, embedder: transformers({ model }) });
82
+ const b64 = Buffer.from(bytes).toString("base64");
83
+ fs.writeFileSync(indexTs, stamp +
84
+ "// AUTO-GENERATED by `kura index` — do not edit. Frozen search index (base64) so the worker\n" +
85
+ "// bundle imports it instead of reading the filesystem (Workers-safe).\n" +
86
+ `export const INDEX_B64 = ${JSON.stringify(b64)};\n`);
87
+ console.log(`kura index: wrote app/_index.ts (${(bytes.length / 1024).toFixed(0)}KB index) in ${((Date.now() - t0) / 1000).toFixed(1)}s`);
88
+ }
89
+ else {
90
+ if (fs.existsSync(indexTs))
91
+ fs.rmSync(indexTs);
92
+ console.log("kura index: --no-embed — MDX only, no search index (runtime search is lexical)");
66
93
  }
67
- const localeTag = locales.size ? ` (+${variants.length} variants across ${locales.size} locales)` : "";
68
- console.log(`kura index: embedding ${DOCS.length} docs${localeTag} (model ${model})…`);
69
- const t0 = Date.now();
70
- const bytes = await buildIndex({ entries: allEntries, embedder: transformers({ model }) });
71
- const b64 = Buffer.from(bytes).toString("base64");
72
- fs.mkdirSync(path.dirname(indexTs), { recursive: true });
73
- fs.writeFileSync(indexTs, "// AUTO-GENERATED by `kura index` — do not edit. Frozen search index (base64) so the worker\n" +
74
- "// bundle imports it instead of reading the filesystem (Workers-safe).\n" +
75
- `export const CONTENT_HASH = ${JSON.stringify(contentHash)};\n` +
76
- `export const INDEX_B64 = ${JSON.stringify(b64)};\n`);
77
- console.log(`kura index: wrote app/_index.ts (${(bytes.length / 1024).toFixed(0)}KB index) in ${((Date.now() - t0) / 1000).toFixed(1)}s`);
78
94
  // Precompile MDX -> static HTML with curated components (build-time; Workers-safe at runtime).
79
95
  // Bucketed by locale: "default" for the flat (default-locale) files, plus one per variant locale.
80
96
  const { mdxToHtml } = await import("@kurajs/docs/mdx");
@@ -93,7 +109,8 @@ async function cmdIndex() {
93
109
  await render("default", d);
94
110
  for (const e of variants)
95
111
  await render(e.locale, e);
96
- fs.writeFileSync(mdxTs, "// AUTO-GENERATED by `kura index` — do not edit. Frozen precompiled MDX (locale → slug → html).\n" +
112
+ fs.writeFileSync(mdxTs, stamp +
113
+ "// AUTO-GENERATED by `kura index` — do not edit. Frozen precompiled MDX (locale → slug → html).\n" +
97
114
  `export const MDX: Record<string, Record<string, string>> = ${JSON.stringify(map)};\n`);
98
115
  const total = Object.values(map).reduce((n, b) => n + Object.keys(b).length, 0);
99
116
  const tag = locales.size ? ` across ${locales.size + 1} locales` : "";
@@ -104,6 +121,8 @@ if (cmd === "index") {
104
121
  await cmdIndex();
105
122
  }
106
123
  else {
107
- console.log("Kura CLI\n kura index [--model <hf-model>] build & freeze the docs search index + MDX");
124
+ console.log("Kura CLI\n" +
125
+ " kura index [--model <hf-model>] build & freeze the docs search index + MDX\n" +
126
+ " kura index --no-embed MDX only, no search index (runtime search is lexical)");
108
127
  process.exit(cmd ? 1 : 0);
109
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kurajs/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "description": "Kura CLI — build the docs search index (kura index).",
6
6
  "bin": {
@@ -15,14 +15,18 @@
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
17
  "@kurajs/core": "^0.0.1",
18
- "@kurajs/docs": "^0.0.2",
19
- "@kurajs/transformers": "^0.0.1"
18
+ "@kurajs/docs": "^0.0.3"
20
19
  },
21
20
  "peerDependencies": {
22
- "@huggingface/transformers": ">=3"
21
+ "@kurajs/transformers": "^0.0.1"
22
+ },
23
+ "peerDependenciesMeta": {
24
+ "@kurajs/transformers": {
25
+ "optional": true
26
+ }
23
27
  },
24
28
  "devDependencies": {
25
- "@huggingface/transformers": "3.8.1",
29
+ "@kurajs/transformers": "*",
26
30
  "typescript": "^5.7.0"
27
31
  },
28
32
  "author": "Lawrence Lin",