@kaleabdenbel/llmweb 1.0.3 → 1.0.5
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 +9 -3
- package/dist/adapters/express.d.mts +8 -0
- package/dist/adapters/express.d.ts +8 -0
- package/dist/adapters/express.js +39 -13
- package/dist/adapters/express.mjs +29 -13
- package/dist/adapters/next.d.mts +21 -0
- package/dist/adapters/next.d.ts +21 -0
- package/dist/adapters/next.js +39 -13
- package/dist/adapters/next.mjs +29 -13
- package/dist/adapters/react.d.mts +24 -0
- package/dist/adapters/react.d.ts +24 -0
- package/dist/adapters/react.js +32 -16
- package/dist/adapters/react.mjs +32 -16
- package/dist/adapters/vanilla.d.mts +8 -0
- package/dist/adapters/vanilla.d.ts +8 -0
- package/dist/adapters/vanilla.js +39 -13
- package/dist/adapters/vanilla.mjs +29 -13
- package/dist/cli.js +562 -0
- package/dist/index-B8G8cw7b.d.mts +50 -0
- package/dist/index-B8G8cw7b.d.ts +50 -0
- package/dist/index.browser.d.mts +10 -0
- package/dist/index.browser.d.ts +10 -0
- package/dist/index.browser.js +12 -7
- package/dist/index.browser.mjs +12 -7
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +39 -15
- package/dist/index.mjs +29 -15
- package/dist/injector-CXZdcvik.d.mts +22 -0
- package/dist/injector-D0jrfa25.d.ts +22 -0
- package/package.json +19 -9
package/dist/cli.js
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var import_commander2 = require("commander");
|
|
28
|
+
|
|
29
|
+
// src/cli/init.ts
|
|
30
|
+
var import_commander = require("commander");
|
|
31
|
+
var import_prompts = __toESM(require("prompts"));
|
|
32
|
+
|
|
33
|
+
// src/generators/config.ts
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
35
|
+
var import_path = __toESM(require("path"));
|
|
36
|
+
async function generateConfig(config) {
|
|
37
|
+
const cwd = process.cwd();
|
|
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"
|
|
51
|
+
};
|
|
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}`);
|
|
91
|
+
const mdPath = import_path.default.join(cwd, "llm.md");
|
|
92
|
+
const mdContent = `# ${staticMetadata.identity.name}
|
|
93
|
+
|
|
94
|
+
> ${staticMetadata.identity.description || "No description provided."}
|
|
95
|
+
|
|
96
|
+
This file is a machine-readable summary of the site structure for LLMs and Agents.
|
|
97
|
+
|
|
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"}
|
|
105
|
+
|
|
106
|
+
## Content Types
|
|
107
|
+
${staticMetadata.contentTypes.map((t) => `- ${t}`).join("\n") || "- None"}
|
|
108
|
+
|
|
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"}
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
Generated by [llmweb](https://github.com/kaleabdenbel/llmweb)
|
|
118
|
+
`;
|
|
119
|
+
import_fs.default.writeFileSync(mdPath, mdContent);
|
|
120
|
+
console.log(`Created documentation: ${mdPath}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/generators/frameworks.ts
|
|
124
|
+
var import_fs2 = __toESM(require("fs"));
|
|
125
|
+
var import_path2 = __toESM(require("path"));
|
|
126
|
+
async function generateFrameworkGlue(config) {
|
|
127
|
+
const cwd = process.cwd();
|
|
128
|
+
const framework = config.framework || "vite";
|
|
129
|
+
console.log(`Detected framework: ${framework}`);
|
|
130
|
+
if (framework === "vite" || framework === "react") {
|
|
131
|
+
const setupContent = `import { LLMSource } from '@kaleabdenbel/llmweb/adapters/react';
|
|
132
|
+
import { config } from './llmweb.config';
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Use this component in your root layout or App component.
|
|
136
|
+
* It will inject the machine-readable identity into your page.
|
|
137
|
+
*/
|
|
138
|
+
export function LLMIdentity() {
|
|
139
|
+
return <LLMSource config={config as any} />;
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
142
|
+
const setupPath = import_path2.default.join(cwd, "llmweb.setup.tsx");
|
|
143
|
+
import_fs2.default.writeFileSync(setupPath, setupContent);
|
|
144
|
+
console.log(`Created: ${setupPath}`);
|
|
145
|
+
console.log("\nTIP: Import and use <LLMIdentity /> in your App.tsx or main.tsx\n");
|
|
146
|
+
} else if (framework === "next") {
|
|
147
|
+
const appDirPath = import_path2.default.join(cwd, "app");
|
|
148
|
+
const srcAppDirPath = import_path2.default.join(cwd, "src", "app");
|
|
149
|
+
let targetAppDir = "";
|
|
150
|
+
if (import_fs2.default.existsSync(appDirPath)) {
|
|
151
|
+
targetAppDir = appDirPath;
|
|
152
|
+
} else if (import_fs2.default.existsSync(srcAppDirPath)) {
|
|
153
|
+
targetAppDir = srcAppDirPath;
|
|
154
|
+
}
|
|
155
|
+
if (targetAppDir) {
|
|
156
|
+
const llmDirPath = import_path2.default.join(targetAppDir, "llm");
|
|
157
|
+
if (!import_fs2.default.existsSync(llmDirPath)) {
|
|
158
|
+
import_fs2.default.mkdirSync(llmDirPath, { recursive: true });
|
|
159
|
+
}
|
|
160
|
+
const pagePath = import_path2.default.join(llmDirPath, "page.tsx");
|
|
161
|
+
const pageContent = `import { LLMSource } from '@kaleabdenbel/llmweb/adapters/next';
|
|
162
|
+
import { config } from '../../llmweb.config';
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Machine-readable identity page.
|
|
166
|
+
* Accessible at /llm
|
|
167
|
+
*/
|
|
168
|
+
export default async function LLMPage() {
|
|
169
|
+
return (
|
|
170
|
+
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
|
|
171
|
+
<h1>LLM Perspective</h1>
|
|
172
|
+
<p>This page provides a machine-readable view of the site structure and dynamic data.</p>
|
|
173
|
+
|
|
174
|
+
<LLMSource config={config as any} />
|
|
175
|
+
|
|
176
|
+
<section style={{ marginTop: '2rem' }}>
|
|
177
|
+
<h2>Raw Configuration</h2>
|
|
178
|
+
<pre style={{ background: '#f4f4f4', padding: '1rem', borderRadius: '4px', overflow: 'auto' }}>
|
|
179
|
+
{JSON.stringify(config, null, 2)}
|
|
180
|
+
</pre>
|
|
181
|
+
</section>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
import_fs2.default.writeFileSync(pagePath, pageContent);
|
|
187
|
+
console.log(`Created: ${pagePath}`);
|
|
188
|
+
console.log("\nTIP: Your machine-readable identity is now available at /llm\n");
|
|
189
|
+
} else {
|
|
190
|
+
const setupContent = `import { LLMSource } from '@kaleabdenbel/llmweb/adapters/next';
|
|
191
|
+
import { config } from './llmweb.config';
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Add this to your root layout.tsx or a dedicated page.
|
|
195
|
+
*/
|
|
196
|
+
export function LLMIdentity() {
|
|
197
|
+
return <LLMSource config={config as any} />;
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
const setupPath = import_path2.default.join(cwd, "llmweb.setup.tsx");
|
|
201
|
+
import_fs2.default.writeFileSync(setupPath, setupContent);
|
|
202
|
+
console.log(`Created: ${setupPath}`);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
console.log("Generic setup: Ensure `llmweb.json` and `llm.md` are served at the root of your site.");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/cli/init.ts
|
|
210
|
+
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("--dynamic <items>", "Comma-separated list of dynamic data types (key=path)").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) => {
|
|
211
|
+
let config = { ...options };
|
|
212
|
+
const silent = !!config.silent;
|
|
213
|
+
const identity = silent && config.name ? {
|
|
214
|
+
name: config.name,
|
|
215
|
+
description: config.description || "",
|
|
216
|
+
brand: config.brand || "",
|
|
217
|
+
locale: config.locale || "en-US"
|
|
218
|
+
} : await (0, import_prompts.default)([
|
|
219
|
+
{
|
|
220
|
+
type: config.name ? null : "text",
|
|
221
|
+
name: "name",
|
|
222
|
+
message: "What is the name of your site?",
|
|
223
|
+
initial: "My Awesome Site"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: config.description ? null : "text",
|
|
227
|
+
name: "description",
|
|
228
|
+
message: "One sentence describing the product/site:",
|
|
229
|
+
initial: config.description
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
type: config.brand ? null : "text",
|
|
233
|
+
name: "brand",
|
|
234
|
+
message: "Brand/organization name?",
|
|
235
|
+
initial: config.brand
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
type: config.locale ? null : "text",
|
|
239
|
+
name: "locale",
|
|
240
|
+
message: "Primary language/locale?",
|
|
241
|
+
initial: config.locale || "en-US"
|
|
242
|
+
}
|
|
243
|
+
]);
|
|
244
|
+
if (config.name) identity.name = config.name;
|
|
245
|
+
if (config.description) identity.description = config.description;
|
|
246
|
+
if (config.brand) identity.brand = config.brand;
|
|
247
|
+
if (config.locale) identity.locale = config.locale || "en-US";
|
|
248
|
+
const navigation = { pages: [] };
|
|
249
|
+
const { hasNav } = silent ? { hasNav: false } : await (0, import_prompts.default)({
|
|
250
|
+
type: "confirm",
|
|
251
|
+
name: "hasNav",
|
|
252
|
+
message: "Do you want to declare main navigation pages?",
|
|
253
|
+
initial: true
|
|
254
|
+
});
|
|
255
|
+
if (hasNav) {
|
|
256
|
+
let addPage = true;
|
|
257
|
+
while (addPage) {
|
|
258
|
+
const page = await (0, import_prompts.default)([
|
|
259
|
+
{
|
|
260
|
+
type: "text",
|
|
261
|
+
name: "label",
|
|
262
|
+
message: "Page label (e.g. Home, Pricing):"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
name: "path",
|
|
267
|
+
message: "Page path (e.g. /, /pricing):"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
type: "confirm",
|
|
271
|
+
name: "more",
|
|
272
|
+
message: "Add another page?",
|
|
273
|
+
initial: false
|
|
274
|
+
}
|
|
275
|
+
]);
|
|
276
|
+
if (page.label && page.path) {
|
|
277
|
+
navigation.pages.push({ label: page.label, path: page.path });
|
|
278
|
+
}
|
|
279
|
+
addPage = page.more;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const business = silent ? {
|
|
283
|
+
address: config.address || "",
|
|
284
|
+
email: config.email || "",
|
|
285
|
+
twitter: config.twitter || "",
|
|
286
|
+
linkedin: config.linkedin || ""
|
|
287
|
+
} : await (0, import_prompts.default)([
|
|
288
|
+
{
|
|
289
|
+
type: config.address ? null : "text",
|
|
290
|
+
name: "address",
|
|
291
|
+
message: "Company/organization address?",
|
|
292
|
+
initial: config.address
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
type: config.email ? null : "text",
|
|
296
|
+
name: "email",
|
|
297
|
+
message: "Contact email?",
|
|
298
|
+
initial: config.email
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
type: config.twitter ? null : "text",
|
|
302
|
+
name: "twitter",
|
|
303
|
+
message: "Twitter URL?",
|
|
304
|
+
initial: config.twitter
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
type: config.linkedin ? null : "text",
|
|
308
|
+
name: "linkedin",
|
|
309
|
+
message: "LinkedIn URL?",
|
|
310
|
+
initial: config.linkedin
|
|
311
|
+
}
|
|
312
|
+
]);
|
|
313
|
+
if (config.address) business.address = config.address;
|
|
314
|
+
if (config.email) business.email = config.email;
|
|
315
|
+
if (config.twitter) business.twitter = config.twitter;
|
|
316
|
+
if (config.linkedin) business.linkedin = config.linkedin;
|
|
317
|
+
const seo = silent ? {
|
|
318
|
+
titlePattern: config.titlePattern || "%s | Site Name",
|
|
319
|
+
descriptionPattern: config.descPattern || "",
|
|
320
|
+
multilingual: !!config.multilingual
|
|
321
|
+
} : await (0, import_prompts.default)([
|
|
322
|
+
{
|
|
323
|
+
type: config.titlePattern ? null : "text",
|
|
324
|
+
name: "titlePattern",
|
|
325
|
+
message: "Default meta title pattern?",
|
|
326
|
+
initial: config.titlePattern || "%s | Site Name"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
type: config.descPattern ? null : "text",
|
|
330
|
+
name: "descriptionPattern",
|
|
331
|
+
message: "Default meta description pattern?",
|
|
332
|
+
initial: config.descPattern
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
type: config.multilingual !== void 0 ? null : "confirm",
|
|
336
|
+
name: "multilingual",
|
|
337
|
+
message: "Do you want multilingual content?",
|
|
338
|
+
initial: false
|
|
339
|
+
}
|
|
340
|
+
]);
|
|
341
|
+
if (config.titlePattern) seo.titlePattern = config.titlePattern;
|
|
342
|
+
if (config.descPattern) seo.descriptionPattern = config.descPattern;
|
|
343
|
+
if (config.multilingual !== void 0) seo.multilingual = config.multilingual;
|
|
344
|
+
const { contentTypes } = silent && config.name ? { contentTypes: [] } : await (0, import_prompts.default)({
|
|
345
|
+
type: "multiselect",
|
|
346
|
+
name: "contentTypes",
|
|
347
|
+
message: "Which content types do you have?",
|
|
348
|
+
choices: [
|
|
349
|
+
{ title: "Blog", value: "blog" },
|
|
350
|
+
{ title: "Articles", value: "articles" },
|
|
351
|
+
{ title: "FAQ", value: "faq" },
|
|
352
|
+
{ title: "Tutorials", value: "tutorials" },
|
|
353
|
+
{ title: "Products", value: "products" },
|
|
354
|
+
{ title: "Events", value: "events" }
|
|
355
|
+
],
|
|
356
|
+
hint: "- Space to select. Return to submit"
|
|
357
|
+
});
|
|
358
|
+
const frameworkInfo = silent && config.name ? {
|
|
359
|
+
type: config.type || "saas",
|
|
360
|
+
framework: config.framework || "next"
|
|
361
|
+
} : await (0, import_prompts.default)([
|
|
362
|
+
{
|
|
363
|
+
type: config.type ? null : "select",
|
|
364
|
+
name: "type",
|
|
365
|
+
message: "Is this marketing, docs, SaaS, or content?",
|
|
366
|
+
choices: [
|
|
367
|
+
{ title: "Marketing", value: "marketing" },
|
|
368
|
+
{ title: "Docs", value: "docs" },
|
|
369
|
+
{ title: "SaaS", value: "saas" },
|
|
370
|
+
{ title: "Content", value: "content" }
|
|
371
|
+
]
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
type: config.framework ? null : "select",
|
|
375
|
+
name: "framework",
|
|
376
|
+
message: "Which framework are you using?",
|
|
377
|
+
choices: [
|
|
378
|
+
{ title: "Vite (React/Vue/Vanilla)", value: "vite" },
|
|
379
|
+
{ title: "Next.js", value: "next" },
|
|
380
|
+
{ title: "Other", value: "other" }
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
]);
|
|
384
|
+
config = {
|
|
385
|
+
...config,
|
|
386
|
+
identity,
|
|
387
|
+
navigation,
|
|
388
|
+
business,
|
|
389
|
+
seo,
|
|
390
|
+
contentTypes,
|
|
391
|
+
type: frameworkInfo.type || config.type || "saas",
|
|
392
|
+
framework: frameworkInfo.framework || config.framework || "next"
|
|
393
|
+
};
|
|
394
|
+
const dynamicSources = {};
|
|
395
|
+
const { confirmDynamic } = silent || typeof config.dynamic === "string" && config.dynamic.length > 0 ? { confirmDynamic: false } : await (0, import_prompts.default)({
|
|
396
|
+
type: "confirm",
|
|
397
|
+
name: "confirmDynamic",
|
|
398
|
+
message: "Do you have dynamic data to declare? (API, Server Actions)",
|
|
399
|
+
initial: false
|
|
400
|
+
});
|
|
401
|
+
if (confirmDynamic) {
|
|
402
|
+
let addDynamic = true;
|
|
403
|
+
while (addDynamic) {
|
|
404
|
+
const dynamicEntry = await (0, import_prompts.default)([
|
|
405
|
+
{
|
|
406
|
+
type: "text",
|
|
407
|
+
name: "key",
|
|
408
|
+
message: "What is the data key? (e.g. announcements, user)"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
type: "select",
|
|
412
|
+
name: "mode",
|
|
413
|
+
message: "How is this data fetched?",
|
|
414
|
+
choices: [
|
|
415
|
+
{ title: "API Endpoint", value: "api" },
|
|
416
|
+
{ title: "Server Action (Next.js)", value: "action" },
|
|
417
|
+
{ title: "Database/Other", value: "other" }
|
|
418
|
+
]
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
type: (prev) => prev === "api" ? "text" : null,
|
|
422
|
+
name: "url",
|
|
423
|
+
message: "API URL:",
|
|
424
|
+
initial: (prev, values) => `/api/${values.key}`
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
type: (prev) => prev === "action" ? "text" : null,
|
|
428
|
+
name: "actionFn",
|
|
429
|
+
message: "Server Action function name:",
|
|
430
|
+
initial: (prev, values) => `get${values.key.charAt(0).toUpperCase() + values.key.slice(1)}`
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
type: "confirm",
|
|
434
|
+
name: "more",
|
|
435
|
+
message: "Add another dynamic source?",
|
|
436
|
+
initial: false
|
|
437
|
+
}
|
|
438
|
+
]);
|
|
439
|
+
if (dynamicEntry.key) {
|
|
440
|
+
if (dynamicEntry.mode === "api") {
|
|
441
|
+
dynamicSources[dynamicEntry.key] = { from: { type: "fetch", url: dynamicEntry.url } };
|
|
442
|
+
} else if (dynamicEntry.mode === "action") {
|
|
443
|
+
dynamicSources[dynamicEntry.key] = { from: { type: "fn", call: `[INTERNAL:${dynamicEntry.actionFn}]` } };
|
|
444
|
+
} else {
|
|
445
|
+
dynamicSources[dynamicEntry.key] = { from: { type: "other" } };
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
addDynamic = dynamicEntry.more;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (typeof config.dynamic === "string" && config.dynamic.length > 0) {
|
|
452
|
+
const flagSources = {};
|
|
453
|
+
config.dynamic.split(",").forEach((pair) => {
|
|
454
|
+
const [key, value] = pair.split("=").map((s) => s.trim());
|
|
455
|
+
if (key && value) {
|
|
456
|
+
if (value.startsWith("action:")) {
|
|
457
|
+
flagSources[key] = { from: { type: "fn", call: `[INTERNAL:${value.replace("action:", "")}]` } };
|
|
458
|
+
} else {
|
|
459
|
+
flagSources[key] = { from: { type: "fetch", url: value } };
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
Object.assign(dynamicSources, flagSources);
|
|
464
|
+
}
|
|
465
|
+
config.dynamic = dynamicSources;
|
|
466
|
+
console.log("Generating configuration...");
|
|
467
|
+
await generateConfig(config);
|
|
468
|
+
console.log("Generating framework glue...");
|
|
469
|
+
await generateFrameworkGlue(config);
|
|
470
|
+
console.log("Done! llmweb is ready.");
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// package.json
|
|
474
|
+
var package_default = {
|
|
475
|
+
name: "@kaleabdenbel/llmweb",
|
|
476
|
+
version: "1.0.4",
|
|
477
|
+
description: "A compiler for LLM-readable truth from static and dynamic sources.",
|
|
478
|
+
publishConfig: {
|
|
479
|
+
access: "public"
|
|
480
|
+
},
|
|
481
|
+
main: "./dist/index.js",
|
|
482
|
+
module: "./dist/index.mjs",
|
|
483
|
+
browser: "./dist/index.browser.mjs",
|
|
484
|
+
types: "./dist/index.d.ts",
|
|
485
|
+
exports: {
|
|
486
|
+
".": {
|
|
487
|
+
browser: "./dist/index.browser.mjs",
|
|
488
|
+
import: "./dist/index.mjs",
|
|
489
|
+
require: "./dist/index.js",
|
|
490
|
+
types: "./dist/index.d.ts"
|
|
491
|
+
},
|
|
492
|
+
"./adapters/next": {
|
|
493
|
+
import: "./dist/adapters/next.mjs",
|
|
494
|
+
require: "./dist/adapters/next.js",
|
|
495
|
+
types: "./dist/adapters/next.d.ts"
|
|
496
|
+
},
|
|
497
|
+
"./adapters/react": {
|
|
498
|
+
import: "./dist/adapters/react.mjs",
|
|
499
|
+
require: "./dist/adapters/react.js",
|
|
500
|
+
types: "./dist/adapters/react.d.ts"
|
|
501
|
+
},
|
|
502
|
+
"./adapters/vanilla": {
|
|
503
|
+
import: "./dist/adapters/vanilla.mjs",
|
|
504
|
+
require: "./dist/adapters/vanilla.js",
|
|
505
|
+
types: "./dist/adapters/vanilla.d.ts"
|
|
506
|
+
},
|
|
507
|
+
"./adapters/express": {
|
|
508
|
+
import: "./dist/adapters/express.mjs",
|
|
509
|
+
require: "./dist/adapters/express.js",
|
|
510
|
+
types: "./dist/adapters/express.d.ts"
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
files: [
|
|
514
|
+
"dist",
|
|
515
|
+
"LICENSE",
|
|
516
|
+
"README.md"
|
|
517
|
+
],
|
|
518
|
+
bin: {
|
|
519
|
+
llmweb: "./dist/cli.js"
|
|
520
|
+
},
|
|
521
|
+
scripts: {
|
|
522
|
+
build: "npm run build:lib && npm run build:cli",
|
|
523
|
+
"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",
|
|
524
|
+
"build:cli": "tsup src/cli.ts --format cjs --no-dts --no-splitting",
|
|
525
|
+
dev: "npm run build",
|
|
526
|
+
lint: "tsc",
|
|
527
|
+
test: "vitest run"
|
|
528
|
+
},
|
|
529
|
+
keywords: [
|
|
530
|
+
"llm",
|
|
531
|
+
"json",
|
|
532
|
+
"nextjs",
|
|
533
|
+
"api",
|
|
534
|
+
"metadata"
|
|
535
|
+
],
|
|
536
|
+
author: "Kaleab Denbel",
|
|
537
|
+
license: "MIT",
|
|
538
|
+
devDependencies: {
|
|
539
|
+
"@types/node": "^20.0.0",
|
|
540
|
+
"@types/prompts": "^2.4.9",
|
|
541
|
+
"@types/react": "^18.0.0",
|
|
542
|
+
next: "^14.0.0",
|
|
543
|
+
react: "^18.0.0",
|
|
544
|
+
tsup: "^8.0.0",
|
|
545
|
+
typescript: "^5.0.0",
|
|
546
|
+
vitest: "^1.0.0"
|
|
547
|
+
},
|
|
548
|
+
peerDependencies: {
|
|
549
|
+
next: ">=13",
|
|
550
|
+
react: ">=18"
|
|
551
|
+
},
|
|
552
|
+
dependencies: {
|
|
553
|
+
commander: "^14.0.3",
|
|
554
|
+
prompts: "^2.4.2"
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// src/cli.ts
|
|
559
|
+
var program = new import_commander2.Command();
|
|
560
|
+
program.name("llmweb").description("A compiler for LLM-readable truth from static and dynamic sources.").version(package_default.version);
|
|
561
|
+
program.addCommand(initCommand);
|
|
562
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
|
|
50
|
+
export type { DynamicSource as D, LLMConfig as L, MapSchema as M, SourceDefinition as S, TransformerOptions as T, SourceType as a };
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
|
|
50
|
+
export type { DynamicSource as D, LLMConfig as L, MapSchema as M, SourceDefinition as S, TransformerOptions as T, SourceType as a };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { L as LLMConfig, T as TransformerOptions } from './index-B8G8cw7b.mjs';
|
|
2
|
+
export { D as DynamicSource, M as MapSchema, S as SourceDefinition, a as SourceType } from './index-B8G8cw7b.mjs';
|
|
3
|
+
export { t as toScriptString, a as toWindowString, b as transform } from './injector-CXZdcvik.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The main "Compiler" for LLM truth (Browser version).
|
|
7
|
+
*/
|
|
8
|
+
declare function createLLMSource(config: LLMConfig, options?: TransformerOptions): Promise<any>;
|
|
9
|
+
|
|
10
|
+
export { LLMConfig, TransformerOptions, createLLMSource };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { L as LLMConfig, T as TransformerOptions } from './index-B8G8cw7b.js';
|
|
2
|
+
export { D as DynamicSource, M as MapSchema, S as SourceDefinition, a as SourceType } from './index-B8G8cw7b.js';
|
|
3
|
+
export { t as toScriptString, a as toWindowString, b as transform } from './injector-D0jrfa25.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The main "Compiler" for LLM truth (Browser version).
|
|
7
|
+
*/
|
|
8
|
+
declare function createLLMSource(config: LLMConfig, options?: TransformerOptions): Promise<any>;
|
|
9
|
+
|
|
10
|
+
export { LLMConfig, TransformerOptions, createLLMSource };
|