llms-ink 0.2.0 → 0.3.0

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 CHANGED
@@ -52,7 +52,27 @@ Building a big site or a video can take minutes. Bound a call with a signal:
52
52
  ### `ink.check(url, { signal? })` → `Promise<CheckResult>`
53
53
 
54
54
  Checks the site's (or docs section's) published llms.txt against the llmstxt.org format and fetches its first
55
- 25 links: `{ site, url, found, status, title, tokens, sections, links, llmsFull, issues, summary, checkedAt }`.
55
+ 25 links: `{ site, url, found, status, title, tokens, sections, links, llmsFull, parsed, issues, summary, checkedAt }`.
56
+
57
+ `parsed` is the file's structure (`null` when nothing was found):
58
+
59
+ ```ts
60
+ { title, summary, details, sections: [{ name, optional, text, links: [{ title, url, notes, line }] }] }
61
+ ```
62
+
63
+ ### `ink.checkText(text, { base?, links?, signal? })` → `Promise<CheckResult>`
64
+
65
+ Parses and checks llms.txt text you already have, e.g. before publishing it:
66
+
67
+ ```ts
68
+ const { parsed, issues } = await ink.checkText(await readFile('public/llms.txt', 'utf8'), {
69
+ base: 'https://example.com/llms.txt', // resolves relative links; reported as url/site
70
+ links: true, // also fetch up to 25 links and look for llms-full.txt
71
+ });
72
+ ```
73
+
74
+ Without `base`, `site` and `url` are `null`; without `base` and `links`, `llmsFull` is `null` and
75
+ `links.checked` is 0. Up to 2 MB.
56
76
 
57
77
  ### `ink.stream(url, { mode?, fresh?, signal? })` → `AsyncGenerator<StreamEvent>`
58
78
 
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * @module
15
15
  */
16
- export declare const VERSION = "0.2.0";
16
+ export declare const VERSION = "0.3.0";
17
17
  export declare const DEFAULT_BASE_URL = "https://llms.ink";
18
18
  /** `llms` for the index (llms.txt), `full` for llms-full.txt. */
19
19
  export type Mode = 'llms' | 'full';
@@ -72,12 +72,40 @@ export interface CheckIssue {
72
72
  line: number | null;
73
73
  message: string;
74
74
  }
75
- /** `GET /api/v0/check`: a published llms.txt checked against the format and its links. */
76
- export interface CheckResult {
77
- /** The site's origin. */
78
- site: string;
79
- /** The llms.txt that was checked (or the last place looked when none was found). */
75
+ /** One link of a section: `- [title](url): notes`. */
76
+ export interface ParsedLink {
77
+ title: string;
78
+ /** Resolved against `base` / the fetched URL when possible, else as written. */
80
79
  url: string;
80
+ /** Text after `:`, trimmed. */
81
+ notes: string | null;
82
+ line: number;
83
+ }
84
+ /** An H2 section of llms.txt. */
85
+ export interface ParsedSection {
86
+ name: string;
87
+ /** The section is named "Optional" (case-insensitive). */
88
+ optional: boolean;
89
+ /** Non-link content of the section. */
90
+ text: string | null;
91
+ links: ParsedLink[];
92
+ }
93
+ /** The structure of an llms.txt file. */
94
+ export interface ParsedLlms {
95
+ /** The H1. */
96
+ title: string | null;
97
+ /** The blockquote, without `> `. */
98
+ summary: string | null;
99
+ /** Markdown between the summary and the first H2. */
100
+ details: string | null;
101
+ sections: ParsedSection[];
102
+ }
103
+ /** `GET` / `POST /api/v0/check`: an llms.txt checked against the format and its links. */
104
+ export interface CheckResult {
105
+ /** The site's origin; null for {@link LlmsInk.checkText} without `base`. */
106
+ site: string | null;
107
+ /** The llms.txt that was checked (or the last place looked when none was found); null for text without `base`. */
108
+ url: string | null;
81
109
  found: boolean;
82
110
  /** HTTP status of the llms.txt request. */
83
111
  status: number;
@@ -87,7 +115,7 @@ export interface CheckResult {
87
115
  sections: number;
88
116
  links: {
89
117
  total: number;
90
- /** Links fetched this run (the first 25). */
118
+ /** Links fetched this run (the first 25); 0 when links weren't checked. */
91
119
  checked: number;
92
120
  /** `status` is 0 when the link didn't answer in time. */
93
121
  broken: Array<{
@@ -95,8 +123,10 @@ export interface CheckResult {
95
123
  status: number;
96
124
  }>;
97
125
  };
98
- /** Whether llms-full.txt is published next to it. */
99
- llmsFull: boolean;
126
+ /** Whether llms-full.txt is published next to it; null when not checked. */
127
+ llmsFull: boolean | null;
128
+ /** The parsed file; null when nothing was found. */
129
+ parsed: ParsedLlms | null;
100
130
  /** Errors first, then warnings and notes, in line order. */
101
131
  issues: CheckIssue[];
102
132
  summary: {
@@ -206,6 +236,14 @@ export interface LlmsOptions {
206
236
  export interface CheckOptions {
207
237
  signal?: AbortSignal;
208
238
  }
239
+ /** Options for {@link LlmsInk.checkText}. */
240
+ export interface CheckTextOptions {
241
+ /** Public URL the text lives at: resolves relative links and is reported as `url` / `site`. */
242
+ base?: string;
243
+ /** Also check up to 25 linked URLs (and llms-full.txt next to `base`). Defaults to `false`. */
244
+ links?: boolean;
245
+ signal?: AbortSignal;
246
+ }
209
247
  /**
210
248
  * A client for https://llms.ink.
211
249
  *
@@ -226,6 +264,8 @@ export declare class LlmsInk {
226
264
  llms(url: string, options?: LlmsOptions): Promise<Result>;
227
265
  /** Checks a site's published llms.txt against the llmstxt.org format and that its links answer. */
228
266
  check(url: string, options?: CheckOptions): Promise<CheckResult>;
267
+ /** Parses and checks llms.txt text you already have (`POST /api/v0/check`); nothing is published. */
268
+ checkText(text: string, options?: CheckTextOptions): Promise<CheckResult>;
229
269
  /**
230
270
  * Like {@link LlmsInk.llms}, as the build happens: yields `progress` and `partial` events, then one
231
271
  * `result`. Errors — including a terminal `error` event — are thrown as {@link LlmsInkError}s.
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * @module
15
15
  */
16
16
  import { env } from "./env.js";
17
- export const VERSION = '0.2.0';
17
+ export const VERSION = '0.3.0';
18
18
  export const DEFAULT_BASE_URL = 'https://llms.ink';
19
19
  /**
20
20
  * An API error, or llms.ink couldn't be reached (then `status` and `code` are undefined).
@@ -99,12 +99,22 @@ export class LlmsInk {
99
99
  * any public link.
100
100
  */
101
101
  async llms(url, options = {}) {
102
- const res = await this.#get('/api/v0/llms', llmsParams(url, options), 'application/json', options.signal);
102
+ const res = await this.#request('/api/v0/llms', llmsParams(url, options), 'application/json', options.signal);
103
103
  return (await this.#json(res));
104
104
  }
105
105
  /** Checks a site's published llms.txt against the llmstxt.org format and that its links answer. */
106
106
  async check(url, options = {}) {
107
- const res = await this.#get('/api/v0/check', { url }, 'application/json', options.signal);
107
+ const res = await this.#request('/api/v0/check', { url }, 'application/json', options.signal);
108
+ return (await this.#json(res));
109
+ }
110
+ /** Parses and checks llms.txt text you already have (`POST /api/v0/check`); nothing is published. */
111
+ async checkText(text, options = {}) {
112
+ const params = {};
113
+ if (options.base)
114
+ params.base = options.base;
115
+ if (options.links)
116
+ params.links = '1';
117
+ const res = await this.#request('/api/v0/check', params, 'application/json', options.signal, text);
108
118
  return (await this.#json(res));
109
119
  }
110
120
  /**
@@ -113,7 +123,7 @@ export class LlmsInk {
113
123
  * Breaking out of the loop cancels the request.
114
124
  */
115
125
  async *stream(url, options = {}) {
116
- const res = await this.#get('/api/v0/llms', llmsParams(url, options), 'application/x-ndjson', options.signal);
126
+ const res = await this.#request('/api/v0/llms', llmsParams(url, options), 'application/x-ndjson', options.signal);
117
127
  if (!res.headers.get('content-type')?.includes('ndjson')) {
118
128
  yield { type: 'result', result: (await this.#json(res)) };
119
129
  return;
@@ -173,16 +183,19 @@ export class LlmsInk {
173
183
  const known = event.type === 'progress' || event.type === 'partial' || event.type === 'result';
174
184
  return known ? event : undefined;
175
185
  }
176
- async #get(path, params, accept, signal) {
186
+ async #request(path, params, accept, signal, body) {
177
187
  const headers = { accept };
188
+ if (body !== undefined)
189
+ headers['content-type'] = 'text/markdown; charset=utf-8';
178
190
  if (!IS_BROWSER)
179
191
  headers['user-agent'] = `llms-ink-js/${VERSION}`;
180
192
  if (this.key)
181
193
  headers.authorization = `Bearer ${this.key}`;
182
- const target = `${this.baseUrl}${path}?${new URLSearchParams(params)}`;
194
+ const query = new URLSearchParams(params).toString();
195
+ const target = `${this.baseUrl}${path}${query ? `?${query}` : ''}`;
183
196
  let res;
184
197
  try {
185
- res = await this.#fetch(target, { headers, signal });
198
+ res = await this.#fetch(target, body === undefined ? { headers, signal } : { method: 'POST', headers, body, signal });
186
199
  }
187
200
  catch (err) {
188
201
  if (isAbort(err))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llms-ink",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Turn any link — site, docs section, page, GitHub repo, PDF or video — into llms.txt and llms-full.txt. Typed client for llms.ink, plus a Vite plugin, an Astro integration and middleware that serve the files from your site. No dependencies.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,6 +25,8 @@
25
25
  "types": "./dist/index.d.ts",
26
26
  "files": ["dist"],
27
27
  "sideEffects": false,
28
+ "repository": { "type": "git", "url": "git+https://github.com/lukax/llms-ink-js.git" },
29
+ "bugs": "https://github.com/lukax/llms-ink-js/issues",
28
30
  "engines": { "node": ">=18" },
29
31
  "scripts": {
30
32
  "build": "rm -rf dist && tsc -p tsconfig.json",