@public-ui/mcp 3.0.7-rc.3 → 4.0.0-alpha.8

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.
@@ -1,269 +0,0 @@
1
- 'use strict';
2
-
3
- const node_url = require('node:url');
4
-
5
- function buildCorsHeaders() {
6
- return {
7
- "Access-Control-Allow-Origin": "*",
8
- "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
9
- "Access-Control-Allow-Headers": "Content-Type"
10
- };
11
- }
12
- const AI_HINTS_KEY = "ai-hints";
13
- const AI_HINTS_MESSAGES = Object.freeze([
14
- "Always register KoliBri Web Components in the browser runtime before rendering them.",
15
- "Choose the integration guide that matches your project setup to load and bundle the components correctly."
16
- ]);
17
- function normalizeHints(value) {
18
- if (Array.isArray(value)) {
19
- return value.length > 0 ? value : AI_HINTS_MESSAGES;
20
- }
21
- if (typeof value === "string") {
22
- const trimmed = value.trim();
23
- return trimmed ? [trimmed] : AI_HINTS_MESSAGES;
24
- }
25
- return AI_HINTS_MESSAGES;
26
- }
27
- function withAiHints(body = {}) {
28
- const normalizedHints = normalizeHints(body[AI_HINTS_KEY]);
29
- return { ...body, [AI_HINTS_KEY]: normalizedHints };
30
- }
31
- function computeCountsFromEntries(entries = []) {
32
- return entries.reduce(
33
- (acc, entry) => {
34
- const kind = entry?.kind ?? "sample";
35
- acc.total += 1;
36
- if (kind === "sample") {
37
- acc.totalSamples += 1;
38
- return acc;
39
- }
40
- if (kind === "doc") {
41
- acc.totalDocs += 1;
42
- }
43
- return acc;
44
- },
45
- { total: 0, totalSamples: 0, totalDocs: 0 }
46
- );
47
- }
48
- function resolveCounts(index) {
49
- if (!index) {
50
- return computeCountsFromEntries();
51
- }
52
- const fallbackCounts = computeCountsFromEntries(index.entries ?? []);
53
- const source = index.counts ?? {};
54
- return {
55
- total: typeof source.total === "number" ? source.total : fallbackCounts.total,
56
- totalSamples: typeof source.totalSamples === "number" ? source.totalSamples : fallbackCounts.totalSamples,
57
- totalDocs: typeof source.totalDocs === "number" ? source.totalDocs : fallbackCounts.totalDocs
58
- };
59
- }
60
- function normalizePathname(pathname) {
61
- if (pathname === "/") {
62
- return pathname;
63
- }
64
- const prefixes = ["/api/mcp", "/mcp"];
65
- for (const prefix of prefixes) {
66
- if (pathname.startsWith(prefix)) {
67
- const suffix = pathname.slice(prefix.length) || "/";
68
- return suffix.startsWith("/") ? suffix : `/${suffix}`;
69
- }
70
- }
71
- return pathname;
72
- }
73
- async function handleApiRequest({ method = "GET", url = "/", getIndex } = {}) {
74
- const baseHeaders = buildCorsHeaders();
75
- const normalizedMethod = method.toUpperCase();
76
- const requestUrl = new node_url.URL(url, "http://localhost");
77
- const pathname = normalizePathname(requestUrl.pathname);
78
- if (normalizedMethod === "OPTIONS") {
79
- return { statusCode: 204, headers: baseHeaders };
80
- }
81
- if ((normalizedMethod === "GET" || normalizedMethod === "POST") && pathname === "/") {
82
- let index;
83
- try {
84
- index = await getIndex();
85
- } catch (error) {
86
- console.warn("[root] Unable to load index for overview response:", error);
87
- }
88
- const counts = resolveCounts(index);
89
- const generatedAt = index?.generatedAt instanceof Date ? index.generatedAt : void 0;
90
- return {
91
- statusCode: 200,
92
- headers: baseHeaders,
93
- body: withAiHints({
94
- message: "KoliBri MCP backend is running.",
95
- endpoints: ["/health", "/samples", "/sample?id=sample/<component>/<sample>", "/docs", "/doc?id=doc/<identifier>"],
96
- totalEntries: counts.total,
97
- totalSamples: counts.totalSamples,
98
- totalDocs: counts.totalDocs,
99
- generatedAt: (generatedAt ?? /* @__PURE__ */ new Date()).toISOString(),
100
- buildMode: index?.buildMode ?? "runtime"
101
- })
102
- };
103
- }
104
- if (normalizedMethod === "GET" && pathname === "/health") {
105
- const index = await getIndex();
106
- const counts = resolveCounts(index);
107
- const isHealthy = counts.total > 0;
108
- console.log("[health] Total entries:", counts.total);
109
- console.log("[health] Sample entries:", counts.totalSamples);
110
- console.log("[health] Doc entries:", counts.totalDocs);
111
- console.log("[health] Index generated at:", index.generatedAt);
112
- console.log("[health] Is healthy:", isHealthy);
113
- return {
114
- statusCode: isHealthy ? 200 : 503,
115
- headers: baseHeaders,
116
- body: withAiHints({
117
- status: isHealthy ? "ok" : "error",
118
- healthy: isHealthy,
119
- totalEntries: counts.total,
120
- totalSamples: counts.totalSamples,
121
- totalDocs: counts.totalDocs,
122
- message: isHealthy ? `System healthy with ${counts.total} entries available` : "No entries found - system may not be properly initialized",
123
- generatedAt: index.generatedAt.toISOString(),
124
- debug: {
125
- indexGeneratedAt: index.generatedAt.toISOString(),
126
- entriesLength: index.entries.length,
127
- firstFewEntries: index.entries.slice(0, 3).map((e) => e.id)
128
- }
129
- })
130
- };
131
- }
132
- if (normalizedMethod === "GET" && pathname === "/samples") {
133
- const index = await getIndex();
134
- const query = requestUrl.searchParams.get("q") ?? "";
135
- const items = index.list(query, { kinds: ["sample"] }).map((entry) => ({
136
- group: entry.group,
137
- id: entry.id,
138
- name: entry.name,
139
- path: entry.path,
140
- kind: entry.kind ?? "sample"
141
- }));
142
- const counts = resolveCounts(index);
143
- return {
144
- statusCode: 200,
145
- headers: baseHeaders,
146
- body: withAiHints({
147
- items,
148
- query,
149
- total: items.length,
150
- totalEntries: counts.total,
151
- totalSamples: counts.totalSamples,
152
- totalDocs: counts.totalDocs,
153
- generatedAt: index.generatedAt.toISOString()
154
- })
155
- };
156
- }
157
- if (normalizedMethod === "GET" && pathname === "/sample") {
158
- const index = await getIndex();
159
- const id = requestUrl.searchParams.get("id");
160
- if (!id) {
161
- return {
162
- statusCode: 400,
163
- headers: baseHeaders,
164
- body: withAiHints({ error: "missing_id" })
165
- };
166
- }
167
- const entry = index.get(id);
168
- if (!entry) {
169
- return {
170
- statusCode: 404,
171
- headers: baseHeaders,
172
- body: withAiHints({ error: "not_found", id })
173
- };
174
- }
175
- if ((entry.kind ?? "sample") !== "sample") {
176
- return {
177
- statusCode: 400,
178
- headers: baseHeaders,
179
- body: withAiHints({ error: "invalid_kind", expected: "sample", actual: entry.kind ?? "sample", id })
180
- };
181
- }
182
- return {
183
- statusCode: 200,
184
- headers: baseHeaders,
185
- body: withAiHints({
186
- id: entry.id,
187
- group: entry.group,
188
- name: entry.name,
189
- path: entry.path,
190
- code: entry.code,
191
- kind: entry.kind ?? "sample"
192
- })
193
- };
194
- }
195
- if (normalizedMethod === "GET" && pathname === "/docs") {
196
- const index = await getIndex();
197
- const query = requestUrl.searchParams.get("q") ?? "";
198
- const items = index.list(query, { kinds: ["doc"] }).map((entry) => ({
199
- group: entry.group,
200
- id: entry.id,
201
- name: entry.name,
202
- path: entry.path,
203
- kind: entry.kind ?? "doc"
204
- }));
205
- const counts = resolveCounts(index);
206
- return {
207
- statusCode: 200,
208
- headers: baseHeaders,
209
- body: withAiHints({
210
- items,
211
- query,
212
- total: items.length,
213
- totalEntries: counts.totalDocs,
214
- totalDocs: counts.totalDocs,
215
- generatedAt: index.generatedAt.toISOString()
216
- })
217
- };
218
- }
219
- if (normalizedMethod === "GET" && pathname === "/doc") {
220
- const index = await getIndex();
221
- const id = requestUrl.searchParams.get("id");
222
- if (!id) {
223
- return {
224
- statusCode: 400,
225
- headers: baseHeaders,
226
- body: withAiHints({ error: "missing_id" })
227
- };
228
- }
229
- const entry = index.get(id);
230
- if (!entry || entry.kind !== "doc") {
231
- return {
232
- statusCode: 404,
233
- headers: baseHeaders,
234
- body: withAiHints({ error: "not_found", id })
235
- };
236
- }
237
- return {
238
- statusCode: 200,
239
- headers: baseHeaders,
240
- body: withAiHints({
241
- id: entry.id,
242
- group: entry.group,
243
- name: entry.name,
244
- path: entry.path,
245
- code: entry.code,
246
- kind: entry.kind ?? "doc"
247
- })
248
- };
249
- }
250
- if (normalizedMethod === "POST" && pathname === "/refresh") {
251
- return {
252
- statusCode: 410,
253
- headers: baseHeaders,
254
- body: withAiHints({
255
- error: "refresh_unavailable",
256
- message: "Die Re-Indexierung ist in bereitgestellten Umgebungen deaktiviert, da die Inhalte bereits vorab eingebettet werden."
257
- })
258
- };
259
- }
260
- return {
261
- statusCode: 404,
262
- headers: baseHeaders,
263
- body: withAiHints({ error: "not_found" })
264
- };
265
- }
266
-
267
- exports.AI_HINTS_KEY = AI_HINTS_KEY;
268
- exports.AI_HINTS_MESSAGES = AI_HINTS_MESSAGES;
269
- exports.handleApiRequest = handleApiRequest;
@@ -1,265 +0,0 @@
1
- import { URL } from 'node:url';
2
-
3
- function buildCorsHeaders() {
4
- return {
5
- "Access-Control-Allow-Origin": "*",
6
- "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
7
- "Access-Control-Allow-Headers": "Content-Type"
8
- };
9
- }
10
- const AI_HINTS_KEY = "ai-hints";
11
- const AI_HINTS_MESSAGES = Object.freeze([
12
- "Always register KoliBri Web Components in the browser runtime before rendering them.",
13
- "Choose the integration guide that matches your project setup to load and bundle the components correctly."
14
- ]);
15
- function normalizeHints(value) {
16
- if (Array.isArray(value)) {
17
- return value.length > 0 ? value : AI_HINTS_MESSAGES;
18
- }
19
- if (typeof value === "string") {
20
- const trimmed = value.trim();
21
- return trimmed ? [trimmed] : AI_HINTS_MESSAGES;
22
- }
23
- return AI_HINTS_MESSAGES;
24
- }
25
- function withAiHints(body = {}) {
26
- const normalizedHints = normalizeHints(body[AI_HINTS_KEY]);
27
- return { ...body, [AI_HINTS_KEY]: normalizedHints };
28
- }
29
- function computeCountsFromEntries(entries = []) {
30
- return entries.reduce(
31
- (acc, entry) => {
32
- const kind = entry?.kind ?? "sample";
33
- acc.total += 1;
34
- if (kind === "sample") {
35
- acc.totalSamples += 1;
36
- return acc;
37
- }
38
- if (kind === "doc") {
39
- acc.totalDocs += 1;
40
- }
41
- return acc;
42
- },
43
- { total: 0, totalSamples: 0, totalDocs: 0 }
44
- );
45
- }
46
- function resolveCounts(index) {
47
- if (!index) {
48
- return computeCountsFromEntries();
49
- }
50
- const fallbackCounts = computeCountsFromEntries(index.entries ?? []);
51
- const source = index.counts ?? {};
52
- return {
53
- total: typeof source.total === "number" ? source.total : fallbackCounts.total,
54
- totalSamples: typeof source.totalSamples === "number" ? source.totalSamples : fallbackCounts.totalSamples,
55
- totalDocs: typeof source.totalDocs === "number" ? source.totalDocs : fallbackCounts.totalDocs
56
- };
57
- }
58
- function normalizePathname(pathname) {
59
- if (pathname === "/") {
60
- return pathname;
61
- }
62
- const prefixes = ["/api/mcp", "/mcp"];
63
- for (const prefix of prefixes) {
64
- if (pathname.startsWith(prefix)) {
65
- const suffix = pathname.slice(prefix.length) || "/";
66
- return suffix.startsWith("/") ? suffix : `/${suffix}`;
67
- }
68
- }
69
- return pathname;
70
- }
71
- async function handleApiRequest({ method = "GET", url = "/", getIndex } = {}) {
72
- const baseHeaders = buildCorsHeaders();
73
- const normalizedMethod = method.toUpperCase();
74
- const requestUrl = new URL(url, "http://localhost");
75
- const pathname = normalizePathname(requestUrl.pathname);
76
- if (normalizedMethod === "OPTIONS") {
77
- return { statusCode: 204, headers: baseHeaders };
78
- }
79
- if ((normalizedMethod === "GET" || normalizedMethod === "POST") && pathname === "/") {
80
- let index;
81
- try {
82
- index = await getIndex();
83
- } catch (error) {
84
- console.warn("[root] Unable to load index for overview response:", error);
85
- }
86
- const counts = resolveCounts(index);
87
- const generatedAt = index?.generatedAt instanceof Date ? index.generatedAt : void 0;
88
- return {
89
- statusCode: 200,
90
- headers: baseHeaders,
91
- body: withAiHints({
92
- message: "KoliBri MCP backend is running.",
93
- endpoints: ["/health", "/samples", "/sample?id=sample/<component>/<sample>", "/docs", "/doc?id=doc/<identifier>"],
94
- totalEntries: counts.total,
95
- totalSamples: counts.totalSamples,
96
- totalDocs: counts.totalDocs,
97
- generatedAt: (generatedAt ?? /* @__PURE__ */ new Date()).toISOString(),
98
- buildMode: index?.buildMode ?? "runtime"
99
- })
100
- };
101
- }
102
- if (normalizedMethod === "GET" && pathname === "/health") {
103
- const index = await getIndex();
104
- const counts = resolveCounts(index);
105
- const isHealthy = counts.total > 0;
106
- console.log("[health] Total entries:", counts.total);
107
- console.log("[health] Sample entries:", counts.totalSamples);
108
- console.log("[health] Doc entries:", counts.totalDocs);
109
- console.log("[health] Index generated at:", index.generatedAt);
110
- console.log("[health] Is healthy:", isHealthy);
111
- return {
112
- statusCode: isHealthy ? 200 : 503,
113
- headers: baseHeaders,
114
- body: withAiHints({
115
- status: isHealthy ? "ok" : "error",
116
- healthy: isHealthy,
117
- totalEntries: counts.total,
118
- totalSamples: counts.totalSamples,
119
- totalDocs: counts.totalDocs,
120
- message: isHealthy ? `System healthy with ${counts.total} entries available` : "No entries found - system may not be properly initialized",
121
- generatedAt: index.generatedAt.toISOString(),
122
- debug: {
123
- indexGeneratedAt: index.generatedAt.toISOString(),
124
- entriesLength: index.entries.length,
125
- firstFewEntries: index.entries.slice(0, 3).map((e) => e.id)
126
- }
127
- })
128
- };
129
- }
130
- if (normalizedMethod === "GET" && pathname === "/samples") {
131
- const index = await getIndex();
132
- const query = requestUrl.searchParams.get("q") ?? "";
133
- const items = index.list(query, { kinds: ["sample"] }).map((entry) => ({
134
- group: entry.group,
135
- id: entry.id,
136
- name: entry.name,
137
- path: entry.path,
138
- kind: entry.kind ?? "sample"
139
- }));
140
- const counts = resolveCounts(index);
141
- return {
142
- statusCode: 200,
143
- headers: baseHeaders,
144
- body: withAiHints({
145
- items,
146
- query,
147
- total: items.length,
148
- totalEntries: counts.total,
149
- totalSamples: counts.totalSamples,
150
- totalDocs: counts.totalDocs,
151
- generatedAt: index.generatedAt.toISOString()
152
- })
153
- };
154
- }
155
- if (normalizedMethod === "GET" && pathname === "/sample") {
156
- const index = await getIndex();
157
- const id = requestUrl.searchParams.get("id");
158
- if (!id) {
159
- return {
160
- statusCode: 400,
161
- headers: baseHeaders,
162
- body: withAiHints({ error: "missing_id" })
163
- };
164
- }
165
- const entry = index.get(id);
166
- if (!entry) {
167
- return {
168
- statusCode: 404,
169
- headers: baseHeaders,
170
- body: withAiHints({ error: "not_found", id })
171
- };
172
- }
173
- if ((entry.kind ?? "sample") !== "sample") {
174
- return {
175
- statusCode: 400,
176
- headers: baseHeaders,
177
- body: withAiHints({ error: "invalid_kind", expected: "sample", actual: entry.kind ?? "sample", id })
178
- };
179
- }
180
- return {
181
- statusCode: 200,
182
- headers: baseHeaders,
183
- body: withAiHints({
184
- id: entry.id,
185
- group: entry.group,
186
- name: entry.name,
187
- path: entry.path,
188
- code: entry.code,
189
- kind: entry.kind ?? "sample"
190
- })
191
- };
192
- }
193
- if (normalizedMethod === "GET" && pathname === "/docs") {
194
- const index = await getIndex();
195
- const query = requestUrl.searchParams.get("q") ?? "";
196
- const items = index.list(query, { kinds: ["doc"] }).map((entry) => ({
197
- group: entry.group,
198
- id: entry.id,
199
- name: entry.name,
200
- path: entry.path,
201
- kind: entry.kind ?? "doc"
202
- }));
203
- const counts = resolveCounts(index);
204
- return {
205
- statusCode: 200,
206
- headers: baseHeaders,
207
- body: withAiHints({
208
- items,
209
- query,
210
- total: items.length,
211
- totalEntries: counts.totalDocs,
212
- totalDocs: counts.totalDocs,
213
- generatedAt: index.generatedAt.toISOString()
214
- })
215
- };
216
- }
217
- if (normalizedMethod === "GET" && pathname === "/doc") {
218
- const index = await getIndex();
219
- const id = requestUrl.searchParams.get("id");
220
- if (!id) {
221
- return {
222
- statusCode: 400,
223
- headers: baseHeaders,
224
- body: withAiHints({ error: "missing_id" })
225
- };
226
- }
227
- const entry = index.get(id);
228
- if (!entry || entry.kind !== "doc") {
229
- return {
230
- statusCode: 404,
231
- headers: baseHeaders,
232
- body: withAiHints({ error: "not_found", id })
233
- };
234
- }
235
- return {
236
- statusCode: 200,
237
- headers: baseHeaders,
238
- body: withAiHints({
239
- id: entry.id,
240
- group: entry.group,
241
- name: entry.name,
242
- path: entry.path,
243
- code: entry.code,
244
- kind: entry.kind ?? "doc"
245
- })
246
- };
247
- }
248
- if (normalizedMethod === "POST" && pathname === "/refresh") {
249
- return {
250
- statusCode: 410,
251
- headers: baseHeaders,
252
- body: withAiHints({
253
- error: "refresh_unavailable",
254
- message: "Die Re-Indexierung ist in bereitgestellten Umgebungen deaktiviert, da die Inhalte bereits vorab eingebettet werden."
255
- })
256
- };
257
- }
258
- return {
259
- statusCode: 404,
260
- headers: baseHeaders,
261
- body: withAiHints({ error: "not_found" })
262
- };
263
- }
264
-
265
- export { AI_HINTS_KEY, AI_HINTS_MESSAGES, handleApiRequest };