@farming-labs/svelte 0.1.59 → 0.1.62

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 (2) hide show
  1. package/dist/server.js +117 -1
  2. package/package.json +2 -2
package/dist/server.js CHANGED
@@ -30,7 +30,7 @@
30
30
  import fs from "node:fs";
31
31
  import path from "node:path";
32
32
  import matter from "gray-matter";
33
- import { applySidebarFolderIndexBehavior, buildDocsAgentDiscoverySpec, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsSkillRequest, normalizeDocsRelated, performDocsSearch, renderDocsMarkdownDocument, renderDocsSkillDocument, stripGeneratedAgentProvenance, resolveDocsAgentMdxContent, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolvePageReadingTime, resolveReadingTimeOptions, resolveDocsSkillFormat, } from "@farming-labs/docs";
33
+ import { applySidebarFolderIndexBehavior, buildDocsAgentDiscoverySpec, emitDocsAnalyticsEvent, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsSkillRequest, normalizeDocsRelated, performDocsSearch, renderDocsMarkdownDocument, renderDocsSkillDocument, stripGeneratedAgentProvenance, resolveDocsAgentMdxContent, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolvePageReadingTime, resolveReadingTimeOptions, resolveDocsSkillFormat, } from "@farming-labs/docs";
34
34
  import { createDocsMcpHttpHandler, resolveDocsMcpConfig, serializeDocsIconRegistry, serializeOpenDocsProviders, } from "@farming-labs/docs/server";
35
35
  import { loadDocsNavTree, loadDocsContent, flattenNavTree } from "./content.js";
36
36
  import { renderMarkdown } from "./markdown.js";
@@ -341,6 +341,7 @@ function findPageInMap(contentMap, dirPrefix, slug) {
341
341
  */
342
342
  export function createDocsServer(config = {}) {
343
343
  const entry = config.entry ?? "docs";
344
+ const analytics = config.analytics;
344
345
  const contentDirBase = config.contentDir ?? entry;
345
346
  const rootDir = path.resolve(config.rootDir ?? process.cwd());
346
347
  const i18n = resolveDocsI18n(config.i18n);
@@ -702,6 +703,7 @@ export function createDocsServer(config = {}) {
702
703
  headers: { "Content-Type": "application/json" },
703
704
  });
704
705
  }
706
+ const searchStartedAt = Date.now();
705
707
  const results = await performDocsSearch({
706
708
  pages: getSearchIndex(ctx),
707
709
  query,
@@ -710,6 +712,20 @@ export function createDocsServer(config = {}) {
710
712
  pathname: event.url.searchParams.get("pathname") ?? undefined,
711
713
  siteTitle: llmsTitle,
712
714
  });
715
+ await emitDocsAnalyticsEvent(analytics, {
716
+ type: "api_search",
717
+ source: "server",
718
+ url: event.request.url,
719
+ path: event.url.pathname,
720
+ locale: ctx.locale,
721
+ input: { query },
722
+ properties: {
723
+ queryLength: query.length,
724
+ resultCount: results.length,
725
+ pathname: event.url.searchParams.get("pathname") ?? undefined,
726
+ durationMs: Math.max(0, Date.now() - searchStartedAt),
727
+ },
728
+ });
713
729
  return new Response(JSON.stringify(results), {
714
730
  headers: { "Content-Type": "application/json" },
715
731
  });
@@ -737,7 +753,16 @@ export function createDocsServer(config = {}) {
737
753
  }
738
754
  const DEFAULT_SYSTEM_PROMPT = buildDefaultSystemPrompt();
739
755
  async function POST(event) {
756
+ const requestUrl = new URL(event.request.url);
757
+ const requestStartedAt = Date.now();
740
758
  if (!aiConfig.enabled) {
759
+ await emitDocsAnalyticsEvent(analytics, {
760
+ type: "api_ai_error",
761
+ source: "server",
762
+ url: event.request.url,
763
+ path: requestUrl.pathname,
764
+ properties: { reason: "disabled" },
765
+ });
741
766
  return new Response(JSON.stringify({
742
767
  error: "AI is not enabled. Set `ai: { enabled: true }` in your docs config to enable it.",
743
768
  }), { status: 404, headers: { "Content-Type": "application/json" } });
@@ -746,6 +771,16 @@ export function createDocsServer(config = {}) {
746
771
  (typeof import.meta !== "undefined" ? import.meta.env?.OPENAI_API_KEY : undefined) ??
747
772
  (typeof process !== "undefined" ? process.env?.OPENAI_API_KEY : undefined);
748
773
  if (!resolvedKey) {
774
+ await emitDocsAnalyticsEvent(analytics, {
775
+ type: "api_ai_error",
776
+ source: "server",
777
+ url: event.request.url,
778
+ path: requestUrl.pathname,
779
+ properties: {
780
+ reason: "missing_api_key",
781
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
782
+ },
783
+ });
749
784
  return new Response(JSON.stringify({
750
785
  error: "AI is enabled but no API key was found. Set `apiKey` in your docs config `ai` section or add OPENAI_API_KEY to your environment.",
751
786
  }), { status: 500, headers: { "Content-Type": "application/json" } });
@@ -756,14 +791,48 @@ export function createDocsServer(config = {}) {
756
791
  body = await event.request.json();
757
792
  }
758
793
  catch {
794
+ await emitDocsAnalyticsEvent(analytics, {
795
+ type: "api_ai_error",
796
+ source: "server",
797
+ url: event.request.url,
798
+ path: requestUrl.pathname,
799
+ locale: ctx.locale,
800
+ properties: {
801
+ reason: "invalid_json",
802
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
803
+ },
804
+ });
759
805
  return new Response(JSON.stringify({ error: "Invalid JSON body. Expected { messages: [...] }" }), { status: 400, headers: { "Content-Type": "application/json" } });
760
806
  }
761
807
  const messages = body.messages;
762
808
  if (!Array.isArray(messages) || messages.length === 0) {
809
+ await emitDocsAnalyticsEvent(analytics, {
810
+ type: "api_ai_error",
811
+ source: "server",
812
+ url: event.request.url,
813
+ path: requestUrl.pathname,
814
+ locale: ctx.locale,
815
+ properties: {
816
+ reason: "missing_messages",
817
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
818
+ },
819
+ });
763
820
  return new Response(JSON.stringify({ error: "messages array is required and must not be empty." }), { status: 400, headers: { "Content-Type": "application/json" } });
764
821
  }
765
822
  const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
766
823
  if (!lastUserMessage) {
824
+ await emitDocsAnalyticsEvent(analytics, {
825
+ type: "api_ai_error",
826
+ source: "server",
827
+ url: event.request.url,
828
+ path: requestUrl.pathname,
829
+ locale: ctx.locale,
830
+ properties: {
831
+ reason: "missing_user_message",
832
+ messageCount: messages.length,
833
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
834
+ },
835
+ });
767
836
  return new Response(JSON.stringify({ error: "At least one user message is required." }), {
768
837
  status: 400,
769
838
  headers: { "Content-Type": "application/json" },
@@ -789,6 +858,20 @@ export function createDocsServer(config = {}) {
789
858
  : undefined;
790
859
  const resolved = resolveAIModelAndProvider(aiConfig, requestedModel);
791
860
  const finalKey = resolved.apiKey ?? resolvedKey;
861
+ await emitDocsAnalyticsEvent(analytics, {
862
+ type: "api_ai_request",
863
+ source: "server",
864
+ url: event.request.url,
865
+ path: requestUrl.pathname,
866
+ locale: ctx.locale,
867
+ input: { question: lastUserMessage.content },
868
+ properties: {
869
+ messageCount: messages.length,
870
+ questionLength: lastUserMessage.content.length,
871
+ retrievedCount: scored.length,
872
+ model: resolved.model,
873
+ },
874
+ });
792
875
  const llmResponse = await fetch(`${resolved.baseUrl}/chat/completions`, {
793
876
  method: "POST",
794
877
  headers: {
@@ -799,8 +882,40 @@ export function createDocsServer(config = {}) {
799
882
  });
800
883
  if (!llmResponse.ok) {
801
884
  const errText = await llmResponse.text().catch(() => "Unknown error");
885
+ await emitDocsAnalyticsEvent(analytics, {
886
+ type: "api_ai_error",
887
+ source: "server",
888
+ url: event.request.url,
889
+ path: requestUrl.pathname,
890
+ locale: ctx.locale,
891
+ input: { question: lastUserMessage.content },
892
+ properties: {
893
+ reason: "llm_error",
894
+ status: llmResponse.status,
895
+ messageCount: messages.length,
896
+ questionLength: lastUserMessage.content.length,
897
+ retrievedCount: scored.length,
898
+ model: resolved.model,
899
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
900
+ },
901
+ });
802
902
  return new Response(JSON.stringify({ error: `LLM API error (${llmResponse.status}): ${errText}` }), { status: 502, headers: { "Content-Type": "application/json" } });
803
903
  }
904
+ await emitDocsAnalyticsEvent(analytics, {
905
+ type: "api_ai_response",
906
+ source: "server",
907
+ url: event.request.url,
908
+ path: requestUrl.pathname,
909
+ locale: ctx.locale,
910
+ input: { question: lastUserMessage.content },
911
+ properties: {
912
+ messageCount: messages.length,
913
+ questionLength: lastUserMessage.content.length,
914
+ retrievedCount: scored.length,
915
+ model: resolved.model,
916
+ durationMs: Math.max(0, Date.now() - requestStartedAt),
917
+ },
918
+ });
804
919
  return new Response(llmResponse.body, {
805
920
  headers: {
806
921
  "Content-Type": "text/event-stream",
@@ -831,6 +946,7 @@ export function createDocsServer(config = {}) {
831
946
  },
832
947
  },
833
948
  mcp: config.mcp,
949
+ analytics,
834
950
  defaultName: mcpSiteTitle,
835
951
  });
836
952
  return { load, GET, POST, MCP };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/svelte",
3
- "version": "0.1.59",
3
+ "version": "0.1.62",
4
4
  "description": "SvelteKit adapter for @farming-labs/docs — content loading and navigation utilities",
5
5
  "keywords": [
6
6
  "docs",
@@ -56,7 +56,7 @@
56
56
  "devDependencies": {
57
57
  "@types/node": "^22.10.0",
58
58
  "typescript": "^5.9.3",
59
- "@farming-labs/docs": "0.1.59"
59
+ "@farming-labs/docs": "0.1.62"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "@farming-labs/docs": "*"