@kaleabdenbel/llmweb 1.0.4 → 1.0.6
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/README.md +10 -3
- package/dist/adapters/express.d.mts +8 -0
- package/dist/adapters/express.d.ts +8 -0
- package/dist/adapters/express.js +200 -0
- package/dist/adapters/express.mjs +163 -0
- package/dist/adapters/next.d.mts +10 -0
- package/dist/adapters/next.d.ts +10 -0
- package/dist/adapters/next.js +299 -0
- package/dist/adapters/next.mjs +261 -0
- package/dist/adapters/react.d.mts +18 -0
- package/dist/adapters/react.d.ts +18 -0
- package/dist/adapters/react.js +336 -0
- package/dist/adapters/react.mjs +300 -0
- package/dist/adapters/vanilla.d.mts +8 -0
- package/dist/adapters/vanilla.d.ts +8 -0
- package/dist/adapters/vanilla.js +201 -0
- package/dist/adapters/vanilla.mjs +164 -0
- package/dist/cli.js +447 -102
- package/dist/index-C5RUlsf2.d.mts +56 -0
- package/dist/index-C5RUlsf2.d.ts +56 -0
- package/dist/index.browser.d.mts +10 -0
- package/dist/index.browser.d.ts +10 -0
- package/dist/index.browser.js +185 -0
- package/dist/index.browser.mjs +154 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +208 -0
- package/dist/index.mjs +167 -0
- package/dist/injector-DD7MSHW4.d.mts +26 -0
- package/dist/injector-Gh9wJgRZ.d.ts +26 -0
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -35,57 +35,89 @@ var import_fs = __toESM(require("fs"));
|
|
|
35
35
|
var import_path = __toESM(require("path"));
|
|
36
36
|
async function generateConfig(config) {
|
|
37
37
|
const cwd = process.cwd();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
config.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
} else if (typeof config.dynamic === "object") {
|
|
53
|
-
dynamicData = config.dynamic;
|
|
54
|
-
}
|
|
55
|
-
const llmWebConfig = {
|
|
56
|
-
name: config.name,
|
|
57
|
-
description: config.description || "",
|
|
58
|
-
type: config.type,
|
|
59
|
-
public: publicData,
|
|
60
|
-
dynamic: dynamicData,
|
|
61
|
-
version: "1.0.0"
|
|
38
|
+
const staticMetadata = {
|
|
39
|
+
identity: {
|
|
40
|
+
name: config.identity?.name || config.name || "My Awesome Site",
|
|
41
|
+
description: config.identity?.description || config.description || "",
|
|
42
|
+
brand: config.identity?.brand || "",
|
|
43
|
+
locale: config.identity?.locale || "en-US"
|
|
44
|
+
},
|
|
45
|
+
navigation: config.navigation || { pages: [] },
|
|
46
|
+
business: config.business || {},
|
|
47
|
+
seo: config.seo || {},
|
|
48
|
+
contentTypes: config.contentTypes || [],
|
|
49
|
+
type: config.type || "marketing",
|
|
50
|
+
version: "1.2.0"
|
|
62
51
|
};
|
|
63
|
-
const
|
|
64
|
-
import_fs.default.writeFileSync(
|
|
65
|
-
console.log(`Created: ${
|
|
52
|
+
const staticJsonPath = import_path.default.join(cwd, "llmweb.json");
|
|
53
|
+
import_fs.default.writeFileSync(staticJsonPath, JSON.stringify(staticMetadata, null, 2));
|
|
54
|
+
console.log(`Created static metadata: ${staticJsonPath}`);
|
|
55
|
+
const isTS = import_fs.default.existsSync(import_path.default.join(cwd, "tsconfig.json"));
|
|
56
|
+
const configExt = isTS ? "ts" : "js";
|
|
57
|
+
const configPath = import_path.default.join(cwd, `llmweb.config.${configExt}`);
|
|
58
|
+
const dynamicEntries = Object.entries(config.dynamic || {});
|
|
59
|
+
const dynamicConfigStr = dynamicEntries.map(([key, source]) => {
|
|
60
|
+
const from = source.from || {};
|
|
61
|
+
let fromStr = "";
|
|
62
|
+
if (from.type === "fetch") {
|
|
63
|
+
fromStr = `{ type: 'fetch', url: '${from.url}' }`;
|
|
64
|
+
} else if (from.type === "fn") {
|
|
65
|
+
const fnName = from.call.replace("[INTERNAL:", "").replace("]", "");
|
|
66
|
+
fromStr = `{ type: 'fn', call: ${fnName} }`;
|
|
67
|
+
} else {
|
|
68
|
+
fromStr = `{ type: 'other' }`;
|
|
69
|
+
}
|
|
70
|
+
return ` ${key}: {
|
|
71
|
+
from: ${fromStr}
|
|
72
|
+
}`;
|
|
73
|
+
}).join(",\n");
|
|
74
|
+
const serverActionImports = dynamicEntries.filter(([_, source]) => source.from?.type === "fn").map(([_, source]) => {
|
|
75
|
+
const fnName = source.from.call.replace("[INTERNAL:", "").replace("]", "");
|
|
76
|
+
return `// import { ${fnName} } from './actions';`;
|
|
77
|
+
}).join("\n");
|
|
78
|
+
const configContent = `${isTS ? "import { LLMConfig } from '@kaleabdenbel/llmweb';" : ""}
|
|
79
|
+
import staticData from './llmweb.json';
|
|
80
|
+
${serverActionImports}
|
|
81
|
+
|
|
82
|
+
export const config${isTS ? ": LLMConfig" : ""} = {
|
|
83
|
+
static: staticData,
|
|
84
|
+
dynamic: {
|
|
85
|
+
${dynamicConfigStr}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
`;
|
|
89
|
+
import_fs.default.writeFileSync(configPath, configContent);
|
|
90
|
+
console.log(`Created orchestrator: ${configPath}`);
|
|
66
91
|
const mdPath = import_path.default.join(cwd, "llm.md");
|
|
67
|
-
const mdContent = `# ${
|
|
92
|
+
const mdContent = `# ${staticMetadata.identity.name}
|
|
68
93
|
|
|
69
|
-
> ${
|
|
94
|
+
> ${staticMetadata.identity.description || "No description provided."}
|
|
70
95
|
|
|
71
96
|
This file is a machine-readable summary of the site structure for LLMs and Agents.
|
|
72
97
|
|
|
73
|
-
##
|
|
74
|
-
${
|
|
98
|
+
## Identity
|
|
99
|
+
- **Brand**: ${staticMetadata.identity.brand || "N/A"}
|
|
100
|
+
- **Locale**: ${staticMetadata.identity.locale}
|
|
101
|
+
- **Type**: ${staticMetadata.type}
|
|
102
|
+
|
|
103
|
+
## Navigation
|
|
104
|
+
${staticMetadata.navigation.pages.map((p) => `- [${p.label}](${p.path})`).join("\n") || "- None"}
|
|
75
105
|
|
|
76
|
-
##
|
|
77
|
-
|
|
78
|
-
${publicData.map((item) => `- ${item}`).join("\n") || "- None"}
|
|
106
|
+
## Content Types
|
|
107
|
+
${staticMetadata.contentTypes.map((t) => `- ${t}`).join("\n") || "- None"}
|
|
79
108
|
|
|
80
|
-
## Dynamic Data
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
## Dynamic Data Sources
|
|
110
|
+
${dynamicEntries.map(([key, source]) => {
|
|
111
|
+
const from = source.from || {};
|
|
112
|
+
const info = from.type === "fetch" ? `API: \`${from.url}\`` : from.type === "fn" ? `Server Action: \`${from.call}\`` : "Other";
|
|
113
|
+
return `- **${key}**: ${info}`;
|
|
114
|
+
}).join("\n") || "- None"}
|
|
83
115
|
|
|
84
116
|
---
|
|
85
117
|
Generated by [llmweb](https://github.com/kaleabdenbel/llmweb)
|
|
86
118
|
`;
|
|
87
119
|
import_fs.default.writeFileSync(mdPath, mdContent);
|
|
88
|
-
console.log(`Created: ${mdPath}`);
|
|
120
|
+
console.log(`Created documentation: ${mdPath}`);
|
|
89
121
|
}
|
|
90
122
|
|
|
91
123
|
// src/generators/frameworks.ts
|
|
@@ -96,95 +128,408 @@ async function generateFrameworkGlue(config) {
|
|
|
96
128
|
const framework = config.framework || "vite";
|
|
97
129
|
console.log(`Detected framework: ${framework}`);
|
|
98
130
|
if (framework === "vite" || framework === "react") {
|
|
99
|
-
const setupContent = `import {
|
|
100
|
-
import config from './llmweb.
|
|
131
|
+
const setupContent = `import { LLMJson } from '@kaleabdenbel/llmweb/adapters/react';
|
|
132
|
+
import { config } from './llmweb.config';
|
|
101
133
|
|
|
102
134
|
/**
|
|
103
135
|
* Use this component in your root layout or App component.
|
|
104
136
|
* It will inject the machine-readable identity into your page.
|
|
105
137
|
*/
|
|
106
138
|
export function LLMIdentity() {
|
|
107
|
-
return <
|
|
139
|
+
return <LLMJson config={config as any} mode="ldjson" />;
|
|
108
140
|
}
|
|
109
141
|
`;
|
|
110
142
|
const setupPath = import_path2.default.join(cwd, "llmweb.setup.tsx");
|
|
111
143
|
import_fs2.default.writeFileSync(setupPath, setupContent);
|
|
112
144
|
console.log(`Created: ${setupPath}`);
|
|
113
|
-
console.log(
|
|
145
|
+
console.log(
|
|
146
|
+
"\nTIP: Import and use <LLMIdentity /> in your App.tsx or main.tsx"
|
|
147
|
+
);
|
|
148
|
+
console.log(
|
|
149
|
+
"Snippet: \n <header>\n <LLMIdentity />\n ...\n </header>\n"
|
|
150
|
+
);
|
|
114
151
|
} else if (framework === "next") {
|
|
115
|
-
const
|
|
116
|
-
|
|
152
|
+
const appDirPath = import_path2.default.join(cwd, "app");
|
|
153
|
+
const srcAppDirPath = import_path2.default.join(cwd, "src", "app");
|
|
154
|
+
let targetAppDir = "";
|
|
155
|
+
if (import_fs2.default.existsSync(appDirPath)) {
|
|
156
|
+
targetAppDir = appDirPath;
|
|
157
|
+
} else if (import_fs2.default.existsSync(srcAppDirPath)) {
|
|
158
|
+
targetAppDir = srcAppDirPath;
|
|
159
|
+
}
|
|
160
|
+
if (targetAppDir) {
|
|
161
|
+
const llmDirPath = import_path2.default.join(targetAppDir, "llm");
|
|
162
|
+
if (!import_fs2.default.existsSync(llmDirPath)) {
|
|
163
|
+
import_fs2.default.mkdirSync(llmDirPath, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
const pagePath = import_path2.default.join(llmDirPath, "page.tsx");
|
|
166
|
+
const pageContent = `import { LLMJson } from '@kaleabdenbel/llmweb/adapters/next';
|
|
167
|
+
import { config } from '../../llmweb.config';
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Machine-readable identity page.
|
|
171
|
+
* Accessible at /llm
|
|
172
|
+
*/
|
|
173
|
+
export default async function LLMPage() {
|
|
174
|
+
return (
|
|
175
|
+
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
|
|
176
|
+
<h1>LLM Perspective</h1>
|
|
177
|
+
<p>This page provides a machine-readable view of the site structure and dynamic data.</p>
|
|
178
|
+
|
|
179
|
+
<LLMJson config={config as any} mode="ldjson" />
|
|
180
|
+
|
|
181
|
+
<section style={{ marginTop: '2rem' }}>
|
|
182
|
+
<h2>Raw Configuration</h2>
|
|
183
|
+
<pre style={{ background: '#f4f4f4', padding: '1rem', borderRadius: '4px', overflow: 'auto' }}>
|
|
184
|
+
{JSON.stringify(config, null, 2)}
|
|
185
|
+
</pre>
|
|
186
|
+
</section>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
import_fs2.default.writeFileSync(pagePath, pageContent);
|
|
192
|
+
console.log(`Created: ${pagePath}`);
|
|
193
|
+
console.log(
|
|
194
|
+
"\nTIP: Your machine-readable identity is now available at /llm\n"
|
|
195
|
+
);
|
|
196
|
+
} else {
|
|
197
|
+
const setupContent = `import { LLMJson } from '@kaleabdenbel/llmweb/adapters/next';
|
|
198
|
+
import { config } from './llmweb.config';
|
|
117
199
|
|
|
118
200
|
/**
|
|
119
|
-
* Add this to your root layout.tsx
|
|
201
|
+
* Add this to your root layout.tsx or a dedicated page.
|
|
120
202
|
*/
|
|
121
203
|
export function LLMIdentity() {
|
|
122
|
-
return <
|
|
204
|
+
return <LLMJson config={config as any} mode="ldjson" />;
|
|
123
205
|
}
|
|
124
206
|
`;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
207
|
+
const setupPath = import_path2.default.join(cwd, "llmweb.setup.tsx");
|
|
208
|
+
import_fs2.default.writeFileSync(setupPath, setupContent);
|
|
209
|
+
console.log(`Created: ${setupPath}`);
|
|
210
|
+
}
|
|
211
|
+
} else if (framework === "static") {
|
|
212
|
+
const indexPath = import_path2.default.join(cwd, "index.html");
|
|
213
|
+
if (import_fs2.default.existsSync(indexPath)) {
|
|
214
|
+
let content = import_fs2.default.readFileSync(indexPath, "utf-8");
|
|
215
|
+
const ldJsonSnippet = `
|
|
216
|
+
<!-- llmweb AI identity -->
|
|
217
|
+
<script type="application/ld+json">
|
|
218
|
+
{
|
|
219
|
+
"@context": "https://schema.org",
|
|
220
|
+
"@type": "WebSite",
|
|
221
|
+
"name": "${config.identity?.name || "My Site"}",
|
|
222
|
+
"description": "${config.identity?.description || ""}"
|
|
223
|
+
}
|
|
224
|
+
</script>`;
|
|
225
|
+
if (content.includes("</head>")) {
|
|
226
|
+
content = content.replace("</head>", `${ldJsonSnippet}
|
|
227
|
+
</head>`);
|
|
228
|
+
} else {
|
|
229
|
+
content = ldJsonSnippet + "\n" + content;
|
|
230
|
+
}
|
|
231
|
+
import_fs2.default.writeFileSync(indexPath, content);
|
|
232
|
+
console.log(`Injected JSON-LD into: ${indexPath}`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(
|
|
235
|
+
"\n[llmweb] Warning: index.html not found for static injection."
|
|
236
|
+
);
|
|
237
|
+
console.log("Please add the following to your <head>:");
|
|
238
|
+
console.log(
|
|
239
|
+
`
|
|
240
|
+
<script type="application/ld+json">
|
|
241
|
+
{ "@context": "https://schema.org", "@type": "WebSite", "name": "${config.identity?.name}" }
|
|
242
|
+
</script>
|
|
243
|
+
`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
128
246
|
} else {
|
|
129
|
-
console.log(
|
|
247
|
+
console.log(
|
|
248
|
+
"Generic setup: Ensure `llmweb.json` and `llm.md` are served at the root of your site."
|
|
249
|
+
);
|
|
130
250
|
}
|
|
131
251
|
}
|
|
132
252
|
|
|
133
253
|
// src/cli/init.ts
|
|
134
|
-
var initCommand = new import_commander.Command("init").description("Initialize llmweb configuration for your project").option("--name <name>", "Project name").option("--type <type>", "Project type (marketing, docs, saas, content)").option("--
|
|
254
|
+
var initCommand = new import_commander.Command("init").description("Initialize llmweb configuration for your project").option("--name <name>", "Project name").option("--description <desc>", "Project description").option("--brand <brand>", "Brand name").option("--locale <locale>", "Locale (e.g. en-US)").option("--type <type>", "Project type (marketing, docs, saas, content)").option("--address <address>", "Business address").option("--email <email>", "Contact email").option("--twitter <url>", "Twitter URL").option("--linkedin <url>", "LinkedIn URL").option("--title-pattern <pattern>", "SEO title pattern").option("--desc-pattern <pattern>", "SEO description pattern").option("--public <items>", "Comma-separated list of public data types").option(
|
|
255
|
+
"--dynamic <items>",
|
|
256
|
+
"Comma-separated list of dynamic data types (key=path)"
|
|
257
|
+
).option("--inject <type>", "Injection method (script, route, component)").option("--framework <framework>", "Framework (vite, next, vue)").option("--silent", "Run in non-interactive mode").action(async (options) => {
|
|
135
258
|
let config = { ...options };
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
259
|
+
const silent = !!config.silent;
|
|
260
|
+
const identity = silent && config.name ? {
|
|
261
|
+
name: config.name,
|
|
262
|
+
description: config.description || "",
|
|
263
|
+
brand: config.brand || "",
|
|
264
|
+
locale: config.locale || "en-US"
|
|
265
|
+
} : await (0, import_prompts.default)([
|
|
266
|
+
{
|
|
267
|
+
type: config.name ? null : "text",
|
|
268
|
+
name: "name",
|
|
269
|
+
message: "What is the name of your site?",
|
|
270
|
+
initial: "My Awesome Site"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
type: config.description ? null : "text",
|
|
274
|
+
name: "description",
|
|
275
|
+
message: "One sentence describing the product/site:",
|
|
276
|
+
initial: config.description
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
type: config.brand ? null : "text",
|
|
280
|
+
name: "brand",
|
|
281
|
+
message: "Brand/organization name?",
|
|
282
|
+
initial: config.brand
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
type: config.locale ? null : "text",
|
|
286
|
+
name: "locale",
|
|
287
|
+
message: "Primary language/locale?",
|
|
288
|
+
initial: config.locale || "en-US"
|
|
289
|
+
}
|
|
290
|
+
]);
|
|
291
|
+
if (config.name) identity.name = config.name;
|
|
292
|
+
if (config.description) identity.description = config.description;
|
|
293
|
+
if (config.brand) identity.brand = config.brand;
|
|
294
|
+
if (config.locale) identity.locale = config.locale || "en-US";
|
|
295
|
+
const navigation = { pages: [] };
|
|
296
|
+
const { hasNav } = silent ? { hasNav: false } : await (0, import_prompts.default)({
|
|
297
|
+
type: "confirm",
|
|
298
|
+
name: "hasNav",
|
|
299
|
+
message: "Do you want to declare main navigation pages?",
|
|
300
|
+
initial: true
|
|
301
|
+
});
|
|
302
|
+
if (hasNav) {
|
|
303
|
+
let addPage = true;
|
|
304
|
+
while (addPage) {
|
|
305
|
+
const page = await (0, import_prompts.default)([
|
|
306
|
+
{
|
|
307
|
+
type: "text",
|
|
308
|
+
name: "label",
|
|
309
|
+
message: "Page label (e.g. Home, Pricing):"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
type: "text",
|
|
313
|
+
name: "path",
|
|
314
|
+
message: "Page path (e.g. /, /pricing):"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
type: "confirm",
|
|
318
|
+
name: "more",
|
|
319
|
+
message: "Add another page?",
|
|
320
|
+
initial: false
|
|
321
|
+
}
|
|
322
|
+
]);
|
|
323
|
+
if (page.label && page.path) {
|
|
324
|
+
navigation.pages.push({ label: page.label, path: page.path });
|
|
182
325
|
}
|
|
183
|
-
|
|
184
|
-
|
|
326
|
+
addPage = page.more;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const business = silent ? {
|
|
330
|
+
address: config.address || "",
|
|
331
|
+
email: config.email || "",
|
|
332
|
+
twitter: config.twitter || "",
|
|
333
|
+
linkedin: config.linkedin || ""
|
|
334
|
+
} : await (0, import_prompts.default)([
|
|
335
|
+
{
|
|
336
|
+
type: config.address ? null : "text",
|
|
337
|
+
name: "address",
|
|
338
|
+
message: "Company/organization address?",
|
|
339
|
+
initial: config.address
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
type: config.email ? null : "text",
|
|
343
|
+
name: "email",
|
|
344
|
+
message: "Contact email?",
|
|
345
|
+
initial: config.email
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
type: config.twitter ? null : "text",
|
|
349
|
+
name: "twitter",
|
|
350
|
+
message: "Twitter URL?",
|
|
351
|
+
initial: config.twitter
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
type: config.linkedin ? null : "text",
|
|
355
|
+
name: "linkedin",
|
|
356
|
+
message: "LinkedIn URL?",
|
|
357
|
+
initial: config.linkedin
|
|
358
|
+
}
|
|
359
|
+
]);
|
|
360
|
+
if (config.address) business.address = config.address;
|
|
361
|
+
if (config.email) business.email = config.email;
|
|
362
|
+
if (config.twitter) business.twitter = config.twitter;
|
|
363
|
+
if (config.linkedin) business.linkedin = config.linkedin;
|
|
364
|
+
const seo = silent ? {
|
|
365
|
+
titlePattern: config.titlePattern || "%s | Site Name",
|
|
366
|
+
descriptionPattern: config.descPattern || "",
|
|
367
|
+
multilingual: !!config.multilingual
|
|
368
|
+
} : await (0, import_prompts.default)([
|
|
369
|
+
{
|
|
370
|
+
type: config.titlePattern ? null : "text",
|
|
371
|
+
name: "titlePattern",
|
|
372
|
+
message: "Default meta title pattern?",
|
|
373
|
+
initial: config.titlePattern || "%s | Site Name"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
type: config.descPattern ? null : "text",
|
|
377
|
+
name: "descriptionPattern",
|
|
378
|
+
message: "Default meta description pattern?",
|
|
379
|
+
initial: config.descPattern
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
type: config.multilingual !== void 0 ? null : "confirm",
|
|
383
|
+
name: "multilingual",
|
|
384
|
+
message: "Do you want multilingual content?",
|
|
385
|
+
initial: false
|
|
386
|
+
}
|
|
387
|
+
]);
|
|
388
|
+
if (config.titlePattern) seo.titlePattern = config.titlePattern;
|
|
389
|
+
if (config.descPattern) seo.descriptionPattern = config.descPattern;
|
|
390
|
+
if (config.multilingual !== void 0)
|
|
391
|
+
seo.multilingual = config.multilingual;
|
|
392
|
+
const { contentTypes } = silent && config.name ? { contentTypes: [] } : await (0, import_prompts.default)({
|
|
393
|
+
type: "multiselect",
|
|
394
|
+
name: "contentTypes",
|
|
395
|
+
message: "Which content types do you have?",
|
|
396
|
+
choices: [
|
|
397
|
+
{ title: "Blog", value: "blog" },
|
|
398
|
+
{ title: "Articles", value: "articles" },
|
|
399
|
+
{ title: "FAQ", value: "faq" },
|
|
400
|
+
{ title: "Tutorials", value: "tutorials" },
|
|
401
|
+
{ title: "Products", value: "products" },
|
|
402
|
+
{ title: "Portfolio", value: "portfolio" },
|
|
403
|
+
{ title: "Events", value: "events" }
|
|
404
|
+
],
|
|
405
|
+
hint: "- Space to select. Return to submit"
|
|
406
|
+
});
|
|
407
|
+
const frameworkInfo = silent && config.name ? {
|
|
408
|
+
type: config.type || "saas",
|
|
409
|
+
framework: config.framework || "next"
|
|
410
|
+
} : await (0, import_prompts.default)([
|
|
411
|
+
{
|
|
412
|
+
type: config.type ? null : "select",
|
|
413
|
+
name: "type",
|
|
414
|
+
message: "Is this marketing, docs, SaaS, or content?",
|
|
415
|
+
choices: [
|
|
416
|
+
{ title: "Marketing", value: "marketing" },
|
|
417
|
+
{ title: "Docs", value: "docs" },
|
|
418
|
+
{ title: "SaaS", value: "saas" },
|
|
419
|
+
{ title: "Content", value: "content" }
|
|
420
|
+
]
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
type: config.framework ? null : "select",
|
|
424
|
+
name: "framework",
|
|
425
|
+
message: "Which framework are you using?",
|
|
426
|
+
choices: [
|
|
427
|
+
{ title: "Vite (React/Vue/Vanilla)", value: "vite" },
|
|
428
|
+
{ title: "Next.js", value: "next" },
|
|
429
|
+
{ title: "Static (HTML)", value: "static" },
|
|
430
|
+
{ title: "Other", value: "other" }
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
]);
|
|
434
|
+
config = {
|
|
435
|
+
...config,
|
|
436
|
+
identity,
|
|
437
|
+
navigation,
|
|
438
|
+
business,
|
|
439
|
+
seo,
|
|
440
|
+
contentTypes,
|
|
441
|
+
type: frameworkInfo.type || config.type || "saas",
|
|
442
|
+
framework: frameworkInfo.framework || config.framework || "next"
|
|
443
|
+
};
|
|
444
|
+
const dynamicSources = {};
|
|
445
|
+
const { confirmDynamic } = silent || typeof config.dynamic === "string" && config.dynamic.length > 0 ? { confirmDynamic: false } : await (0, import_prompts.default)({
|
|
446
|
+
type: "confirm",
|
|
447
|
+
name: "confirmDynamic",
|
|
448
|
+
message: "Do you have dynamic data to declare? (API, Server Actions)",
|
|
449
|
+
initial: false
|
|
450
|
+
});
|
|
451
|
+
if (confirmDynamic) {
|
|
452
|
+
let addDynamic = true;
|
|
453
|
+
while (addDynamic) {
|
|
454
|
+
const dynamicEntry = await (0, import_prompts.default)([
|
|
455
|
+
{
|
|
456
|
+
type: "text",
|
|
457
|
+
name: "key",
|
|
458
|
+
message: "What is the data key? (e.g. announcements, user)"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
type: "select",
|
|
462
|
+
name: "mode",
|
|
463
|
+
message: "How is this data fetched?",
|
|
464
|
+
choices: (prev, values) => {
|
|
465
|
+
const base = [
|
|
466
|
+
{ title: "API Endpoint", value: "api" },
|
|
467
|
+
{ title: "Database/Other", value: "other" }
|
|
468
|
+
];
|
|
469
|
+
if (config.framework === "next") {
|
|
470
|
+
base.splice(1, 0, {
|
|
471
|
+
title: "Server Action (Next.js)",
|
|
472
|
+
value: "action"
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
return base;
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
type: (prev) => prev === "api" ? "text" : null,
|
|
480
|
+
name: "url",
|
|
481
|
+
message: "API URL:",
|
|
482
|
+
initial: (prev, values) => `/api/${values.key}`
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
type: (prev) => prev === "action" ? "text" : null,
|
|
486
|
+
name: "actionFn",
|
|
487
|
+
message: "Server Action function name:",
|
|
488
|
+
initial: (prev, values) => `get${values.key.charAt(0).toUpperCase() + values.key.slice(1)}`
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
type: "confirm",
|
|
492
|
+
name: "more",
|
|
493
|
+
message: "Add another dynamic source?",
|
|
494
|
+
initial: false
|
|
495
|
+
}
|
|
496
|
+
]);
|
|
497
|
+
if (dynamicEntry.key) {
|
|
498
|
+
if (dynamicEntry.mode === "api") {
|
|
499
|
+
dynamicSources[dynamicEntry.key] = {
|
|
500
|
+
from: { type: "fetch", url: dynamicEntry.url }
|
|
501
|
+
};
|
|
502
|
+
} else if (dynamicEntry.mode === "action") {
|
|
503
|
+
dynamicSources[dynamicEntry.key] = {
|
|
504
|
+
from: { type: "fn", call: `[INTERNAL:${dynamicEntry.actionFn}]` }
|
|
505
|
+
};
|
|
506
|
+
} else {
|
|
507
|
+
dynamicSources[dynamicEntry.key] = { from: { type: "other" } };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
addDynamic = dynamicEntry.more;
|
|
511
|
+
}
|
|
185
512
|
}
|
|
186
513
|
if (typeof config.dynamic === "string" && config.dynamic.length > 0) {
|
|
514
|
+
const flagSources = {};
|
|
515
|
+
config.dynamic.split(",").forEach((pair) => {
|
|
516
|
+
const [key, value] = pair.split("=").map((s) => s.trim());
|
|
517
|
+
if (key && value) {
|
|
518
|
+
if (value.startsWith("action:")) {
|
|
519
|
+
flagSources[key] = {
|
|
520
|
+
from: {
|
|
521
|
+
type: "fn",
|
|
522
|
+
call: `[INTERNAL:${value.replace("action:", "")}]`
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
} else {
|
|
526
|
+
flagSources[key] = { from: { type: "fetch", url: value } };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
Object.assign(dynamicSources, flagSources);
|
|
187
531
|
}
|
|
532
|
+
config.dynamic = dynamicSources;
|
|
188
533
|
console.log("Generating configuration...");
|
|
189
534
|
await generateConfig(config);
|
|
190
535
|
console.log("Generating framework glue...");
|
|
@@ -195,7 +540,7 @@ var initCommand = new import_commander.Command("init").description("Initialize l
|
|
|
195
540
|
// package.json
|
|
196
541
|
var package_default = {
|
|
197
542
|
name: "@kaleabdenbel/llmweb",
|
|
198
|
-
version: "1.0.
|
|
543
|
+
version: "1.0.5",
|
|
199
544
|
description: "A compiler for LLM-readable truth from static and dynamic sources.",
|
|
200
545
|
publishConfig: {
|
|
201
546
|
access: "public"
|
|
@@ -243,8 +588,8 @@ var package_default = {
|
|
|
243
588
|
scripts: {
|
|
244
589
|
build: "npm run build:lib && npm run build:cli",
|
|
245
590
|
"build:lib": "tsup src/index.ts src/index.browser.ts src/adapters/next.tsx src/adapters/react.tsx src/adapters/vanilla.ts src/adapters/express.ts --format cjs,esm --dts --clean --no-splitting",
|
|
246
|
-
"build:cli": "tsup src/cli.ts --format cjs --no-dts --
|
|
247
|
-
dev: "npm run build
|
|
591
|
+
"build:cli": "tsup src/cli.ts --format cjs --no-dts --no-splitting",
|
|
592
|
+
dev: "npm run build",
|
|
248
593
|
lint: "tsc",
|
|
249
594
|
test: "vitest run"
|
|
250
595
|
},
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
type SourceType = "fetch" | "fn";
|
|
2
|
+
interface SourceDefinition {
|
|
3
|
+
type: SourceType;
|
|
4
|
+
url?: string;
|
|
5
|
+
call?: (...args: any[]) => Promise<any> | any;
|
|
6
|
+
}
|
|
7
|
+
interface MapSchema {
|
|
8
|
+
[targetKey: string]: string | ((sourceData: any) => any) | MapSchema;
|
|
9
|
+
}
|
|
10
|
+
interface DynamicSource {
|
|
11
|
+
from: SourceDefinition;
|
|
12
|
+
map?: MapSchema;
|
|
13
|
+
}
|
|
14
|
+
interface LLMConfig {
|
|
15
|
+
identity?: {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
brand?: string;
|
|
19
|
+
locale?: string;
|
|
20
|
+
};
|
|
21
|
+
navigation?: {
|
|
22
|
+
pages: Array<{
|
|
23
|
+
label: string;
|
|
24
|
+
path: string;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
business?: {
|
|
28
|
+
address?: string;
|
|
29
|
+
email?: string;
|
|
30
|
+
twitter?: string;
|
|
31
|
+
linkedin?: string;
|
|
32
|
+
};
|
|
33
|
+
seo?: {
|
|
34
|
+
titlePattern?: string;
|
|
35
|
+
descriptionPattern?: string;
|
|
36
|
+
multilingual?: boolean;
|
|
37
|
+
};
|
|
38
|
+
contentTypes?: string[];
|
|
39
|
+
type?: string;
|
|
40
|
+
static?: string | Record<string, any>;
|
|
41
|
+
dynamic?: {
|
|
42
|
+
[key: string]: DynamicSource;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
interface TransformerOptions {
|
|
46
|
+
failLoudly?: boolean;
|
|
47
|
+
timeout?: number;
|
|
48
|
+
}
|
|
49
|
+
interface LLMJsonProps {
|
|
50
|
+
config: LLMConfig;
|
|
51
|
+
mode?: "script" | "window" | "pre" | "ldjson";
|
|
52
|
+
targetKey?: string;
|
|
53
|
+
className?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type { DynamicSource as D, LLMConfig as L, MapSchema as M, SourceDefinition as S, TransformerOptions as T, LLMJsonProps as a, SourceType as b };
|