@ahtmljs/next 0.1.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.
Files changed (49) hide show
  1. package/dist/extractors/data-attrs.d.ts +26 -0
  2. package/dist/extractors/data-attrs.d.ts.map +1 -0
  3. package/dist/extractors/data-attrs.js +163 -0
  4. package/dist/extractors/data-attrs.js.map +1 -0
  5. package/dist/extractors/index.d.ts +12 -0
  6. package/dist/extractors/index.d.ts.map +1 -0
  7. package/dist/extractors/index.js +12 -0
  8. package/dist/extractors/index.js.map +1 -0
  9. package/dist/extractors/merge.d.ts +13 -0
  10. package/dist/extractors/merge.d.ts.map +1 -0
  11. package/dist/extractors/merge.js +25 -0
  12. package/dist/extractors/merge.js.map +1 -0
  13. package/dist/extractors/opengraph.d.ts +7 -0
  14. package/dist/extractors/opengraph.d.ts.map +1 -0
  15. package/dist/extractors/opengraph.js +89 -0
  16. package/dist/extractors/opengraph.js.map +1 -0
  17. package/dist/extractors/schema-org.d.ts +9 -0
  18. package/dist/extractors/schema-org.d.ts.map +1 -0
  19. package/dist/extractors/schema-org.js +104 -0
  20. package/dist/extractors/schema-org.js.map +1 -0
  21. package/dist/handler.d.ts +43 -0
  22. package/dist/handler.d.ts.map +1 -0
  23. package/dist/handler.js +139 -0
  24. package/dist/handler.js.map +1 -0
  25. package/dist/index.d.ts +50 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +48 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/llms-txt.d.ts +42 -0
  30. package/dist/llms-txt.d.ts.map +1 -0
  31. package/dist/llms-txt.js +88 -0
  32. package/dist/llms-txt.js.map +1 -0
  33. package/dist/mcp.d.ts +30 -0
  34. package/dist/mcp.d.ts.map +1 -0
  35. package/dist/mcp.js +64 -0
  36. package/dist/mcp.js.map +1 -0
  37. package/dist/openapi.d.ts +13 -0
  38. package/dist/openapi.d.ts.map +1 -0
  39. package/dist/openapi.js +74 -0
  40. package/dist/openapi.js.map +1 -0
  41. package/dist/policy.d.ts +24 -0
  42. package/dist/policy.d.ts.map +1 -0
  43. package/dist/policy.js +79 -0
  44. package/dist/policy.js.map +1 -0
  45. package/dist/well-known.d.ts +40 -0
  46. package/dist/well-known.d.ts.map +1 -0
  47. package/dist/well-known.js +56 -0
  48. package/dist/well-known.js.map +1 -0
  49. package/package.json +33 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Per-route snapshot handler factory.
3
+ *
4
+ * Usage in a Next.js App Router project:
5
+ *
6
+ * // app/ahtml/[...path]/route.ts
7
+ * import { createAHTMLRoute } from '@ahtmljs/next/handler';
8
+ * import { buildSnapshotForPath } from '../../lib/ahtml';
9
+ * export const { GET, HEAD } = createAHTMLRoute(buildSnapshotForPath);
10
+ *
11
+ * The handler supports:
12
+ * - Content negotiation:
13
+ * Accept: application/ahtml+json → canonical JSON
14
+ * Accept: application/ahtml+text → token-optimal compact text (default)
15
+ * - Conditional GET via If-None-Match → 304
16
+ * - Diff endpoint via ?since=<etag> → SnapshotDiff
17
+ * - ETag, Cache-Control, Last-Modified headers
18
+ * - Optional policy enforcement (rate limit / auth gate)
19
+ */
20
+ import { toJson, toCompact, computeEtag, diff, } from '@ahtmljs/schema';
21
+ import { getConfig } from './index.js';
22
+ import { enforcePolicy } from './policy.js';
23
+ /** A simple in-memory store for previous snapshots (per process) so the
24
+ * diff endpoint can answer "what changed since <etag>?" without the
25
+ * caller re-uploading. Sites with multiple instances should plug in
26
+ * their own KV store via setSnapshotCache. */
27
+ const _cache = new Map();
28
+ export function setSnapshotCache(impl) {
29
+ // override
30
+ _cache._impl = impl;
31
+ }
32
+ function cacheGet(key) {
33
+ const impl = _cache._impl;
34
+ return impl ? impl.get(key) : _cache.get(key);
35
+ }
36
+ function cacheSet(key, s) {
37
+ const impl = _cache._impl;
38
+ if (impl)
39
+ impl.set(key, s);
40
+ else
41
+ _cache.set(key, s);
42
+ }
43
+ export function createAHTMLRoute(builder, configOverride) {
44
+ async function GET(req, ctx) {
45
+ const config = configOverride ?? getConfig();
46
+ const params = await ctx.params;
47
+ const segments = params.path ?? [];
48
+ const policyDecision = await enforcePolicy(req, config);
49
+ if (policyDecision.deny)
50
+ return policyDecision.response;
51
+ let snap;
52
+ try {
53
+ snap = await builder(segments, req);
54
+ }
55
+ catch (err) {
56
+ return error(500, 'snapshot_build_failed', err);
57
+ }
58
+ if (!snap) {
59
+ return error(404, 'no_snapshot', `no snapshot for /${segments.join('/')}`);
60
+ }
61
+ snap = ensureDefaults(snap, config);
62
+ const etag = snap.etag ?? computeEtag(snap);
63
+ snap.etag = etag;
64
+ const cacheKey = snap.url;
65
+ const url = new URL(req.url);
66
+ // Diff endpoint: GET /ahtml/...?since=W/"abc"
67
+ const sinceEtag = url.searchParams.get('since');
68
+ if (sinceEtag) {
69
+ const prev = cacheGet(cacheKey);
70
+ if (prev && (prev.etag === sinceEtag || computeEtag(prev) === sinceEtag)) {
71
+ const d = diff(prev, snap);
72
+ cacheSet(cacheKey, snap);
73
+ return new Response(JSON.stringify(d), {
74
+ status: 200,
75
+ headers: {
76
+ 'content-type': 'application/ahtml-diff+json',
77
+ etag,
78
+ 'cache-control': cacheControl(snap, config),
79
+ 'x-ahtml-version': '0.1',
80
+ },
81
+ });
82
+ }
83
+ // Fall through to full snapshot if we don't have the prior.
84
+ }
85
+ // Conditional GET
86
+ const ifNoneMatch = req.headers.get('if-none-match');
87
+ if (ifNoneMatch && ifNoneMatch === etag) {
88
+ return new Response(null, {
89
+ status: 304,
90
+ headers: { etag, 'cache-control': cacheControl(snap, config) },
91
+ });
92
+ }
93
+ cacheSet(cacheKey, snap);
94
+ const fmt = pickFormat(req);
95
+ const body = fmt === 'json' ? toJson(snap) : toCompact(snap);
96
+ return new Response(body, {
97
+ status: 200,
98
+ headers: {
99
+ 'content-type': fmt === 'json' ? 'application/ahtml+json' : 'application/ahtml+text; charset=utf-8',
100
+ etag,
101
+ 'cache-control': cacheControl(snap, config),
102
+ 'last-modified': new Date(snap.fetched_at).toUTCString(),
103
+ 'x-ahtml-version': '0.1',
104
+ vary: 'Accept',
105
+ },
106
+ });
107
+ }
108
+ async function HEAD(req, ctx) {
109
+ const res = await GET(req, ctx);
110
+ return new Response(null, { status: res.status, headers: res.headers });
111
+ }
112
+ return { GET, HEAD };
113
+ }
114
+ function ensureDefaults(snap, config) {
115
+ if (config.policy && !snap.policy)
116
+ snap.policy = config.policy;
117
+ if (config.default_ttl && snap.ttl == null)
118
+ snap.ttl = config.default_ttl;
119
+ return snap;
120
+ }
121
+ function cacheControl(snap, config) {
122
+ const ttl = snap.ttl ?? config.default_ttl ?? 60;
123
+ return `public, max-age=${ttl}, must-revalidate`;
124
+ }
125
+ function pickFormat(req) {
126
+ const accept = req.headers.get('accept') ?? '';
127
+ if (/application\/ahtml\+json/.test(accept))
128
+ return 'json';
129
+ if (/application\/ahtml\+text/.test(accept))
130
+ return 'compact';
131
+ if (/application\/json/.test(accept) && !/text/.test(accept))
132
+ return 'json';
133
+ // Default: compact text. Maximally token-efficient for LLM agents.
134
+ return 'compact';
135
+ }
136
+ function error(status, code, detail) {
137
+ return new Response(JSON.stringify({ error: code, detail: detail instanceof Error ? detail.message : String(detail) }), { status, headers: { 'content-type': 'application/json' } });
138
+ }
139
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @ahtmljs/next — Next.js plugin.
3
+ *
4
+ * Quickstart:
5
+ *
6
+ * // next.config.js
7
+ * import { withAHTML } from '@ahtmljs/next';
8
+ * export default withAHTML({
9
+ * // your existing next config
10
+ * }, {
11
+ * site: 'https://shop.example.com',
12
+ * policy: {
13
+ * agents_welcome: true,
14
+ * license: 'CC-BY-4.0',
15
+ * rate_limit: '100/min',
16
+ * contact: 'agents@example.com',
17
+ * },
18
+ * });
19
+ *
20
+ * // app/ahtml/[...path]/route.ts
21
+ * import { createAHTMLRoute } from '@ahtmljs/next/handler';
22
+ * import { buildSnapshot } from '../../lib/ahtml';
23
+ * export const { GET, HEAD } = createAHTMLRoute(buildSnapshot);
24
+ *
25
+ * // app/.well-known/ahtml.json/route.ts
26
+ * import { createWellKnownRoute } from '@ahtmljs/next/well-known';
27
+ * export const { GET } = createWellKnownRoute();
28
+ */
29
+ import type { Policy } from '@ahtmljs/schema';
30
+ export interface AHTMLConfig {
31
+ site: string;
32
+ policy?: Policy;
33
+ /** Default TTL in seconds applied to snapshots that don't set their own. */
34
+ default_ttl?: number;
35
+ /** Routes that should appear in /.well-known/ahtml.json. */
36
+ routes?: Array<{
37
+ path: string;
38
+ page_type: string;
39
+ }>;
40
+ /** Emit MCP tools at /ahtml/mcp.json — default true. */
41
+ emit_mcp?: boolean;
42
+ /** Emit OpenAPI at /ahtml/openapi.json — default true. */
43
+ emit_openapi?: boolean;
44
+ }
45
+ export declare function withAHTML<T extends Record<string, unknown>>(nextConfig: T, ahtmlConfig: AHTMLConfig): T & {
46
+ __ahtml: AHTMLConfig;
47
+ };
48
+ export declare function getConfig(): AHTMLConfig;
49
+ export type { Policy };
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAID,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACzD,UAAU,EAAE,CAAC,EACb,WAAW,EAAE,WAAW,GACvB,CAAC,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAM9B;AAED,wBAAgB,SAAS,IAAI,WAAW,CAQvC;AAED,YAAY,EAAE,MAAM,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @ahtmljs/next — Next.js plugin.
3
+ *
4
+ * Quickstart:
5
+ *
6
+ * // next.config.js
7
+ * import { withAHTML } from '@ahtmljs/next';
8
+ * export default withAHTML({
9
+ * // your existing next config
10
+ * }, {
11
+ * site: 'https://shop.example.com',
12
+ * policy: {
13
+ * agents_welcome: true,
14
+ * license: 'CC-BY-4.0',
15
+ * rate_limit: '100/min',
16
+ * contact: 'agents@example.com',
17
+ * },
18
+ * });
19
+ *
20
+ * // app/ahtml/[...path]/route.ts
21
+ * import { createAHTMLRoute } from '@ahtmljs/next/handler';
22
+ * import { buildSnapshot } from '../../lib/ahtml';
23
+ * export const { GET, HEAD } = createAHTMLRoute(buildSnapshot);
24
+ *
25
+ * // app/.well-known/ahtml.json/route.ts
26
+ * import { createWellKnownRoute } from '@ahtmljs/next/well-known';
27
+ * export const { GET } = createWellKnownRoute();
28
+ */
29
+ let _config;
30
+ export function withAHTML(nextConfig, ahtmlConfig) {
31
+ _config = ahtmlConfig;
32
+ if (typeof globalThis !== 'undefined') {
33
+ globalThis.__ahtml_config = ahtmlConfig;
34
+ }
35
+ return { ...nextConfig, __ahtml: ahtmlConfig };
36
+ }
37
+ export function getConfig() {
38
+ if (_config)
39
+ return _config;
40
+ const g = globalThis.__ahtml_config;
41
+ if (g)
42
+ return g;
43
+ return {
44
+ site: '',
45
+ policy: { agents_welcome: true },
46
+ };
47
+ }
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAiBH,IAAI,OAAgC,CAAC;AAErC,MAAM,UAAU,SAAS,CACvB,UAAa,EACb,WAAwB;IAExB,OAAO,GAAG,WAAW,CAAC;IACtB,IAAI,OAAO,UAAU,KAAK,WAAW,EAAE,CAAC;QACrC,UAA+C,CAAC,cAAc,GAAG,WAAW,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,MAAM,CAAC,GAAI,UAA+C,CAAC,cAAc,CAAC;IAC1E,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChB,OAAO;QACL,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * llms.txt emitter — compatibility shim with Jeremy Howard's convention
3
+ * (Sept 2024). ~10% of sites adopted as of May 2026; used by Cursor /
4
+ * Continue / Cline / Mintlify-style IDE agents.
5
+ *
6
+ * AHTML is a strict superset. We auto-emit /llms.txt from the same
7
+ * configured routes that feed /.well-known/ahtml.json — adopters get
8
+ * both lanes for free, with zero extra work.
9
+ *
10
+ * Format reference: https://llmstxt.org
11
+ *
12
+ * # Site Name
13
+ *
14
+ * > One-line description of the site
15
+ *
16
+ * ## Section
17
+ *
18
+ * - [Page title](url): one-line description
19
+ */
20
+ import { type AHTMLConfig } from './index.js';
21
+ export interface LlmsTxtConfig {
22
+ /** The H1 of the file. Defaults to URL host. */
23
+ title?: string;
24
+ /** Blockquote one-liner under the title. */
25
+ description?: string;
26
+ /** Optional content groupings — defaults to one untitled section. */
27
+ sections?: Array<{
28
+ name: string;
29
+ items?: Array<{
30
+ title: string;
31
+ url: string;
32
+ description?: string;
33
+ }>;
34
+ }>;
35
+ /** Optional pointer to fuller AHTML manifest. */
36
+ ahtml_manifest_url?: string;
37
+ }
38
+ export declare function buildLlmsTxt(cfg: LlmsTxtConfig): string;
39
+ export declare function createLlmsTxtRoute(cfgFn?: () => LlmsTxtConfig | Promise<LlmsTxtConfig>, configOverride?: AHTMLConfig): {
40
+ GET: (_req: Request) => Promise<Response>;
41
+ };
42
+ //# sourceMappingURL=llms-txt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt.d.ts","sourceRoot":"","sources":["../src/llms-txt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACrE,CAAC,CAAC;IACH,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA+BvD;AAED,wBAAgB,kBAAkB,CAChC,KAAK,CAAC,EAAE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,EACpD,cAAc,CAAC,EAAE,WAAW;gBAEH,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;EA6BrD"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * llms.txt emitter — compatibility shim with Jeremy Howard's convention
3
+ * (Sept 2024). ~10% of sites adopted as of May 2026; used by Cursor /
4
+ * Continue / Cline / Mintlify-style IDE agents.
5
+ *
6
+ * AHTML is a strict superset. We auto-emit /llms.txt from the same
7
+ * configured routes that feed /.well-known/ahtml.json — adopters get
8
+ * both lanes for free, with zero extra work.
9
+ *
10
+ * Format reference: https://llmstxt.org
11
+ *
12
+ * # Site Name
13
+ *
14
+ * > One-line description of the site
15
+ *
16
+ * ## Section
17
+ *
18
+ * - [Page title](url): one-line description
19
+ */
20
+ import { getConfig } from './index.js';
21
+ export function buildLlmsTxt(cfg) {
22
+ const lines = [];
23
+ lines.push(`# ${cfg.title ?? 'Site'}`);
24
+ lines.push('');
25
+ if (cfg.description) {
26
+ lines.push(`> ${cfg.description}`);
27
+ lines.push('');
28
+ }
29
+ const sections = cfg.sections && cfg.sections.length
30
+ ? cfg.sections
31
+ : [{ name: 'Pages', items: [] }];
32
+ for (const section of sections) {
33
+ lines.push(`## ${section.name}`);
34
+ lines.push('');
35
+ for (const item of section.items ?? []) {
36
+ const tail = item.description ? `: ${item.description}` : '';
37
+ lines.push(`- [${item.title}](${item.url})${tail}`);
38
+ }
39
+ lines.push('');
40
+ }
41
+ if (cfg.ahtml_manifest_url) {
42
+ lines.push('## Machine-readable');
43
+ lines.push('');
44
+ lines.push(`- [AHTML manifest](${cfg.ahtml_manifest_url}): Structured semantic snapshots, typed actions, MCP-compatible tools, OpenAPI`);
45
+ lines.push('');
46
+ }
47
+ return lines.join('\n');
48
+ }
49
+ export function createLlmsTxtRoute(cfgFn, configOverride) {
50
+ async function GET(_req) {
51
+ const ahtmlCfg = configOverride ?? getConfig();
52
+ const cfg = cfgFn
53
+ ? await cfgFn()
54
+ : {
55
+ title: hostFromUrl(ahtmlCfg.site),
56
+ description: ahtmlCfg.policy?.contact ? `Agents welcome — contact: ${ahtmlCfg.policy.contact}` : undefined,
57
+ sections: [
58
+ {
59
+ name: 'Pages',
60
+ items: (ahtmlCfg.routes ?? []).map((r) => ({
61
+ title: r.path,
62
+ url: ahtmlCfg.site.replace(/\/$/, '') + r.path,
63
+ description: r.page_type,
64
+ })),
65
+ },
66
+ ],
67
+ ahtml_manifest_url: ahtmlCfg.site.replace(/\/$/, '') + '/.well-known/ahtml.json',
68
+ };
69
+ const body = buildLlmsTxt(cfg);
70
+ return new Response(body, {
71
+ status: 200,
72
+ headers: {
73
+ 'content-type': 'text/markdown; charset=utf-8',
74
+ 'cache-control': 'public, max-age=300',
75
+ },
76
+ });
77
+ }
78
+ return { GET };
79
+ }
80
+ function hostFromUrl(u) {
81
+ try {
82
+ return new URL(u).host;
83
+ }
84
+ catch {
85
+ return u;
86
+ }
87
+ }
88
+ //# sourceMappingURL=llms-txt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llms-txt.js","sourceRoot":"","sources":["../src/llms-txt.ts"],"names":[],"mappings":"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"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Emit MCP (Model Context Protocol) tool definitions from AHTML actions.
3
+ *
4
+ * Any AHTML snapshot already carries typed action contracts. By emitting
5
+ * those at /ahtml/mcp.json, the site automatically exposes itself as an
6
+ * MCP server-compatible tool surface — no separate MCP server required.
7
+ */
8
+ import type { Snapshot } from '@ahtmljs/schema';
9
+ export interface McpToolDefinition {
10
+ name: string;
11
+ description: string;
12
+ inputSchema: Record<string, unknown>;
13
+ annotations?: Record<string, unknown>;
14
+ }
15
+ export interface McpManifest {
16
+ schema_version: '0.1';
17
+ server: {
18
+ name: string;
19
+ url: string;
20
+ };
21
+ tools: McpToolDefinition[];
22
+ }
23
+ export declare function snapshotsToMcp(server: {
24
+ name: string;
25
+ url: string;
26
+ }, snaps: Snapshot[]): McpManifest;
27
+ export declare function createMcpRoute(getAllSnapshots: () => Snapshot[] | Promise<Snapshot[]>): {
28
+ GET: (req: Request) => Promise<Response>;
29
+ };
30
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,KAAK,CAAC;IACtB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,WAAW,CAYpG;AA0BD,wBAAgB,cAAc,CAAC,eAAe,EAAE,MAAM,QAAQ,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;eAC5D,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;EAapD"}
package/dist/mcp.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Emit MCP (Model Context Protocol) tool definitions from AHTML actions.
3
+ *
4
+ * Any AHTML snapshot already carries typed action contracts. By emitting
5
+ * those at /ahtml/mcp.json, the site automatically exposes itself as an
6
+ * MCP server-compatible tool surface — no separate MCP server required.
7
+ */
8
+ export function snapshotsToMcp(server, snaps) {
9
+ const tools = [];
10
+ const seen = new Set();
11
+ for (const s of snaps) {
12
+ for (const a of s.actions) {
13
+ const name = `${s.page_type}.${a.id}`;
14
+ if (seen.has(name))
15
+ continue;
16
+ seen.add(name);
17
+ tools.push(actionToTool(name, a, s));
18
+ }
19
+ }
20
+ return { schema_version: '0.1', server, tools };
21
+ }
22
+ function actionToTool(name, a, s) {
23
+ const inputSchema = (a.input && '$ref' in a.input ? { type: 'object' } : a.input) ?? {
24
+ type: 'object',
25
+ properties: {},
26
+ };
27
+ const annotations = {};
28
+ if (a.auth)
29
+ annotations.auth = a.auth;
30
+ if (a.cost)
31
+ annotations.cost = a.cost;
32
+ if (a.reversible)
33
+ annotations.reversible = a.reversible;
34
+ if (a.side_effects)
35
+ annotations.side_effects = a.side_effects;
36
+ if (a.confirmation)
37
+ annotations.confirmation = a.confirmation;
38
+ if (a.rate_limit)
39
+ annotations.rate_limit = a.rate_limit;
40
+ if (a.execute_url)
41
+ annotations.execute_url = a.execute_url;
42
+ if (a.preview_url)
43
+ annotations.preview_url = a.preview_url;
44
+ annotations.snapshot_url = s.url;
45
+ return {
46
+ name,
47
+ description: a.label ?? `${a.category ?? 'action'} on ${s.page_type}`,
48
+ inputSchema: inputSchema,
49
+ annotations,
50
+ };
51
+ }
52
+ export function createMcpRoute(getAllSnapshots) {
53
+ async function GET(req) {
54
+ const url = new URL(req.url);
55
+ const snaps = await getAllSnapshots();
56
+ const m = snapshotsToMcp({ name: 'ahtml', url: `${url.protocol}//${url.host}` }, snaps);
57
+ return new Response(JSON.stringify(m, null, 2), {
58
+ status: 200,
59
+ headers: { 'content-type': 'application/json' },
60
+ });
61
+ }
62
+ return { GET };
63
+ }
64
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Emit an OpenAPI 3.1 document describing the snapshot endpoints and
3
+ * any action endpoints declared on snapshots' actions.
4
+ */
5
+ import type { Snapshot } from '@ahtmljs/schema';
6
+ export declare function snapshotsToOpenApi(opts: {
7
+ title: string;
8
+ baseUrl: string;
9
+ }, snaps: Snapshot[]): Record<string, unknown>;
10
+ export declare function createOpenApiRoute(getAllSnapshots: () => Snapshot[] | Promise<Snapshot[]>): {
11
+ GET: (req: Request) => Promise<Response>;
12
+ };
13
+ //# sourceMappingURL=openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0DvH;AAED,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,MAAM,QAAQ,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;eAChE,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;EAapD"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Emit an OpenAPI 3.1 document describing the snapshot endpoints and
3
+ * any action endpoints declared on snapshots' actions.
4
+ */
5
+ export function snapshotsToOpenApi(opts, snaps) {
6
+ const paths = {};
7
+ for (const s of snaps) {
8
+ const path = s.url.replace(opts.baseUrl, '') || '/';
9
+ const ahtmlPath = '/ahtml' + path;
10
+ paths[ahtmlPath] = {
11
+ get: {
12
+ summary: `AHTML snapshot of ${path}`,
13
+ responses: {
14
+ '200': {
15
+ description: 'snapshot',
16
+ content: {
17
+ 'application/ahtml+text': { schema: { type: 'string' } },
18
+ 'application/ahtml+json': { schema: { $ref: 'https://ahtml.dev/schema/v0.1/snapshot.json' } },
19
+ },
20
+ },
21
+ '304': { description: 'not modified' },
22
+ },
23
+ },
24
+ };
25
+ }
26
+ for (const s of snaps) {
27
+ for (const a of s.actions) {
28
+ if (!a.execute_url)
29
+ continue;
30
+ const verb = (a.method ?? 'post').toLowerCase();
31
+ const path = (paths[a.execute_url] ??= {});
32
+ path[verb] = {
33
+ summary: a.label ?? a.id,
34
+ operationId: a.id,
35
+ security: a.auth && a.auth !== 'none' ? [{ bearer: [] }] : undefined,
36
+ requestBody: a.input ? { content: { 'application/json': { schema: a.input } } } : undefined,
37
+ responses: {
38
+ '200': {
39
+ description: 'success',
40
+ ...(a.output && { content: { 'application/json': { schema: a.output } } }),
41
+ },
42
+ },
43
+ 'x-ahtml-cost': a.cost,
44
+ 'x-ahtml-reversible': a.reversible,
45
+ 'x-ahtml-side-effects': a.side_effects,
46
+ 'x-ahtml-confirmation': a.confirmation,
47
+ };
48
+ }
49
+ }
50
+ return {
51
+ openapi: '3.1.0',
52
+ info: { title: opts.title, version: '0.1' },
53
+ servers: [{ url: opts.baseUrl }],
54
+ paths,
55
+ components: {
56
+ securitySchemes: {
57
+ bearer: { type: 'http', scheme: 'bearer' },
58
+ },
59
+ },
60
+ };
61
+ }
62
+ export function createOpenApiRoute(getAllSnapshots) {
63
+ async function GET(req) {
64
+ const url = new URL(req.url);
65
+ const snaps = await getAllSnapshots();
66
+ const doc = snapshotsToOpenApi({ title: 'AHTML', baseUrl: `${url.protocol}//${url.host}` }, snaps);
67
+ return new Response(JSON.stringify(doc, null, 2), {
68
+ status: 200,
69
+ headers: { 'content-type': 'application/json' },
70
+ });
71
+ }
72
+ return { GET };
73
+ }
74
+ //# sourceMappingURL=openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Policy enforcement at the route handler edge.
3
+ *
4
+ * AHTML is opt-in. Sites that don't want agents do not install this plugin
5
+ * and continue to ship HTML behind their existing defenses (CAPTCHA,
6
+ * Cloudflare, etc). Sites that DO want agents install the plugin and use
7
+ * this layer to set the terms:
8
+ *
9
+ * - Identity: agents present a user-agent and (optionally) a signed
10
+ * identity token from the AI provider.
11
+ * - Rate limit: per-IP token bucket.
12
+ * - Auth: certain action endpoints require OAuth2 bearer tokens.
13
+ *
14
+ * This module ONLY enforces the read side (snapshot fetch). Action
15
+ * execution is enforced by the host application's own action handlers
16
+ * — AHTML just publishes the *contract* that says auth is required.
17
+ */
18
+ import type { AHTMLConfig } from './index.js';
19
+ export interface PolicyDecision {
20
+ deny: boolean;
21
+ response: Response;
22
+ }
23
+ export declare function enforcePolicy(req: Request, config: AHTMLConfig): Promise<PolicyDecision>;
24
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAS9C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAOD,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAe9F"}