@farming-labs/next 0.2.40 → 0.2.42

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/dist/api.d.mts CHANGED
@@ -1,8 +1,13 @@
1
1
  import { createDocsAPI as createDocsAPI$1, createDocsMCPAPI as createDocsMCPAPI$1 } from "@farming-labs/theme/api";
2
+ import { DocsCloudRouteHandlerOptions, DocsCloudRouteHandlerOptions as DocsCloudRouteHandlerOptions$1, DocsCloudRouteHandlers, DocsCloudServer, DocsCloudServer as DocsCloudServer$1, createDocsCloudRouteHandler } from "@farming-labs/docs/cloud/server";
2
3
 
3
4
  //#region src/api.d.ts
4
5
  type DocsAPIOptions = NonNullable<Parameters<typeof createDocsAPI$1>[0]>;
5
6
  type DocsMCPAPIOptions = NonNullable<Parameters<typeof createDocsMCPAPI$1>[0]>;
7
+ interface DocsAPICloudOptions extends DocsCloudRouteHandlerOptions$1 {
8
+ docsCloud?: DocsCloudServer$1;
9
+ }
10
+ type DocsAPICloudIntegration = DocsCloudServer$1 | DocsAPICloudOptions;
6
11
  /**
7
12
  * Resolve the app project root for a Next route module.
8
13
  *
@@ -10,7 +15,7 @@ type DocsMCPAPIOptions = NonNullable<Parameters<typeof createDocsMCPAPI$1>[0]>;
10
15
  * output (`.next/server/app/...` or custom-dist-dir `/server/app/...`).
11
16
  */
12
17
  declare function resolveNextProjectRoot(metaUrl: string): string;
13
- declare function createDocsAPI(options?: DocsAPIOptions): {
18
+ declare function createDocsAPI(options?: DocsAPIOptions, cloudIntegration?: DocsAPICloudIntegration): {
14
19
  GET(request: Request): Promise<Response>;
15
20
  POST(request: Request): Promise<Response>;
16
21
  };
@@ -20,4 +25,4 @@ declare function createDocsMCPAPI(options?: DocsMCPAPIOptions): {
20
25
  DELETE(request: Request): Promise<Response>;
21
26
  };
22
27
  //#endregion
23
- export { createDocsAPI, createDocsMCPAPI, resolveNextProjectRoot };
28
+ export { DocsAPICloudIntegration, DocsAPICloudOptions, type DocsCloudRouteHandlerOptions, type DocsCloudRouteHandlers, type DocsCloudServer, createDocsAPI, createDocsCloudRouteHandler, createDocsMCPAPI, resolveNextProjectRoot };
package/dist/api.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
3
  import { createDocsAPI as createDocsAPI$1, createDocsMCPAPI as createDocsMCPAPI$1 } from "@farming-labs/theme/api";
4
+ import { createDocsCloudRouteHandler } from "@farming-labs/docs/cloud/server";
4
5
 
5
6
  //#region src/api.ts
6
7
  /**
@@ -47,12 +48,87 @@ function inferNextProjectRootFromCaller() {
47
48
  Error.prepareStackTrace = previousPrepareStackTrace;
48
49
  }
49
50
  }
50
- function createDocsAPI(options = {}) {
51
+ function isRecord(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
54
+ function normalizeAction(value) {
55
+ return value?.trim().toLowerCase().replace(/_/g, "-") || void 0;
56
+ }
57
+ function isDocsCloudServer(value) {
58
+ return isRecord(value) && typeof value.handleRequest === "function";
59
+ }
60
+ function resolveDocsCloudIntegration(integration) {
61
+ if (!integration) return void 0;
62
+ if (isDocsCloudServer(integration)) return {
63
+ docsCloud: integration,
64
+ routeOptions: {}
65
+ };
66
+ if (!integration.docsCloud) return void 0;
67
+ return {
68
+ docsCloud: integration.docsCloud,
69
+ routeOptions: {
70
+ locale: integration.locale,
71
+ publicBaseUrl: integration.publicBaseUrl
72
+ }
73
+ };
74
+ }
75
+ function isDocsCloudGetRequest(request) {
76
+ const url = new URL(request.url);
77
+ const cloud = normalizeAction(url.searchParams.get("cloud"));
78
+ const action = normalizeAction(url.searchParams.get("action"));
79
+ const format = normalizeAction(url.searchParams.get("format"));
80
+ return cloud === "config" || cloud === "public-config" || action === "cloud-config" || action === "docs-cloud-config" || format === "cloud-config" || format === "docs-cloud-config";
81
+ }
82
+ function isDocsCloudAction(action) {
83
+ return Boolean(action && [
84
+ "analytics",
85
+ "track",
86
+ "track-event",
87
+ "event",
88
+ "ask-ai",
89
+ "ai",
90
+ "chat",
91
+ "docs-cloud"
92
+ ].includes(action));
93
+ }
94
+ async function readJson(request) {
95
+ try {
96
+ return await request.clone().json();
97
+ } catch {
98
+ return;
99
+ }
100
+ }
101
+ async function isDocsCloudPostRequest(request) {
102
+ const url = new URL(request.url);
103
+ const cloud = normalizeAction(url.searchParams.get("cloud"));
104
+ const action = normalizeAction(url.searchParams.get("action"));
105
+ if (isDocsCloudAction(cloud) || isDocsCloudAction(action)) return true;
106
+ const body = await readJson(request);
107
+ if (!isRecord(body)) return false;
108
+ if (isDocsCloudAction(normalizeAction(typeof body.action === "string" ? body.action : void 0))) return true;
109
+ if (typeof body.type === "string") return true;
110
+ if (isRecord(body.event) && typeof body.event.type === "string") return true;
111
+ if (isRecord(body.payload) && typeof body.payload.type === "string") return true;
112
+ return false;
113
+ }
114
+ function createDocsAPI(options = {}, cloudIntegration) {
51
115
  const rootDir = options.rootDir ?? inferNextProjectRootFromCaller();
52
- return createDocsAPI$1(rootDir ? {
116
+ const handlers = createDocsAPI$1(rootDir ? {
53
117
  ...options,
54
118
  rootDir
55
119
  } : options);
120
+ const integration = resolveDocsCloudIntegration(cloudIntegration);
121
+ if (!integration) return handlers;
122
+ return {
123
+ GET(request) {
124
+ if (isDocsCloudGetRequest(request)) return integration.docsCloud.handleRequest(request, integration.routeOptions);
125
+ return handlers.GET(request);
126
+ },
127
+ async POST(request) {
128
+ if (await isDocsCloudPostRequest(request)) return integration.docsCloud.handleRequest(request, integration.routeOptions);
129
+ return handlers.POST(request);
130
+ }
131
+ };
56
132
  }
57
133
  function createDocsMCPAPI(options = {}) {
58
134
  const rootDir = options.rootDir ?? inferNextProjectRootFromCaller();
@@ -63,4 +139,4 @@ function createDocsMCPAPI(options = {}) {
63
139
  }
64
140
 
65
141
  //#endregion
66
- export { createDocsAPI, createDocsMCPAPI, resolveNextProjectRoot };
142
+ export { createDocsAPI, createDocsCloudRouteHandler, createDocsMCPAPI, resolveNextProjectRoot };
@@ -3,6 +3,7 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
3
  //#region src/client-callbacks.d.ts
4
4
  declare function DocsClientCallbacks(props?: {
5
5
  apiReferencePrimaryServerUrl?: string;
6
+ docsCloudEnabled?: boolean;
6
7
  }): react_jsx_runtime0.JSX.Element;
7
8
  //#endregion
8
9
  export { DocsClientCallbacks as default };
@@ -31,14 +31,32 @@ function DocsClientCallbacks(props) {
31
31
  timers.forEach((timer) => window.clearTimeout(timer));
32
32
  };
33
33
  }, [props?.apiReferencePrimaryServerUrl]);
34
+ const analytics = resolveDocsClientAnalytics(docsConfig.analytics, props?.docsCloudEnabled);
34
35
  return /* @__PURE__ */ jsx(DocsClientHooks, {
35
36
  onCopyClick: docsConfig.onCopyClick,
36
- analytics: docsConfig.analytics,
37
+ analytics,
37
38
  onFeedback: docsConfig.feedback && typeof docsConfig.feedback === "object" ? docsConfig.feedback.onFeedback : void 0,
38
39
  onAIFeedback: docsConfig.ai?.feedback && typeof docsConfig.ai.feedback === "object" ? docsConfig.ai.feedback.onFeedback : void 0,
39
40
  onAIActions: docsConfig.ai?.onActions
40
41
  });
41
42
  }
43
+ function resolveDocsClientAnalytics(analytics, docsCloudEnabled) {
44
+ if (!docsCloudEnabled) return analytics;
45
+ if (analytics === true) return {
46
+ enabled: true,
47
+ console: true,
48
+ cloud: false
49
+ };
50
+ if (analytics && typeof analytics === "object") return {
51
+ ...analytics,
52
+ cloud: false
53
+ };
54
+ return {
55
+ enabled: true,
56
+ console: false,
57
+ cloud: false
58
+ };
59
+ }
42
60
 
43
61
  //#endregion
44
62
  export { DocsClientCallbacks as default };
package/dist/config.mjs CHANGED
@@ -79,8 +79,13 @@ const DOCS_API_ROUTE_TEMPLATE = `\
79
79
  ${GENERATED_BANNER}
80
80
  import docsConfig from "@/docs.config";
81
81
  import { createDocsAPI } from "@farming-labs/next/api";
82
+ import { createDocsCloudServer } from "@farming-labs/docs/cloud/server";
82
83
 
83
- export const { GET, POST } = createDocsAPI(docsConfig);
84
+ const docsCloud = createDocsCloudServer({
85
+ config: docsConfig,
86
+ });
87
+
88
+ export const { GET, POST } = createDocsAPI(docsConfig, docsCloud);
84
89
 
85
90
  export const revalidate = false;
86
91
  `;
@@ -139,7 +144,6 @@ const FILE_EXTS = [
139
144
  "js"
140
145
  ];
141
146
  const INTERNAL_DOCS_CONFIG_ALIAS = "@farming-labs/next-internal-docs-config";
142
- const DEFAULT_DOCS_CLOUD_ANALYTICS_ENDPOINT = "https://api.farming-labs.dev/v1/analytics/events";
143
147
  const NEXT_PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
144
148
  const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
145
149
  const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
@@ -212,11 +216,11 @@ function normalizeEnvValue(value) {
212
216
  }
213
217
  function createPublicDocsCloudAnalyticsEnv() {
214
218
  const projectId = normalizeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID) ?? normalizeEnvValue(process.env.PUBLIC_DOCS_CLOUD_PROJECT_ID) ?? normalizeEnvValue(process.env.DOCS_CLOUD_PROJECT_ID);
215
- const endpoint = normalizeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT) ?? normalizeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT) ?? normalizeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_ENDPOINT) ?? (projectId ? DEFAULT_DOCS_CLOUD_ANALYTICS_ENDPOINT : void 0);
219
+ const configuredEndpoint = normalizeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT) ?? normalizeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT) ?? normalizeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_ENDPOINT);
216
220
  const enabled = normalizeEnvValue(process.env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED) ?? normalizeEnvValue(process.env.PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED) ?? normalizeEnvValue(process.env.DOCS_CLOUD_ANALYTICS_ENABLED);
217
221
  const env = {};
218
222
  if (projectId) env.NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID = projectId;
219
- if (endpoint) env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT = endpoint;
223
+ if (configuredEndpoint) env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT = configuredEndpoint;
220
224
  if (enabled) env.NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED = enabled;
221
225
  return env;
222
226
  }
@@ -250,6 +254,17 @@ function createDocsWorkspaceAliases(root, workspaceRoot) {
250
254
  "src",
251
255
  "server.ts"
252
256
  ]),
257
+ "@farming-labs/docs/cloud/server": workspaceEntrypoint([
258
+ "packages",
259
+ "docs",
260
+ "dist",
261
+ "docs-cloud-server.mjs"
262
+ ], [
263
+ "packages",
264
+ "docs",
265
+ "src",
266
+ "docs-cloud-server.ts"
267
+ ]),
253
268
  "@farming-labs/next": workspaceEntrypoint([
254
269
  "packages",
255
270
  "next",
@@ -1830,6 +1845,7 @@ function withDocs(nextConfig = {}) {
1830
1845
  if (workspaceRoot) Object.assign(resolvedConfig.resolve.alias, {
1831
1846
  "@farming-labs/docs$": join(workspaceRoot, "packages", "docs", "dist", "index.mjs"),
1832
1847
  "@farming-labs/docs/server": join(workspaceRoot, "packages", "docs", "dist", "server.mjs"),
1848
+ "@farming-labs/docs/cloud/server": join(workspaceRoot, "packages", "docs", "dist", "docs-cloud-server.mjs"),
1833
1849
  "@farming-labs/next$": join(workspaceRoot, "packages", "next", "dist", "index.mjs"),
1834
1850
  "@farming-labs/next/api": join(workspaceRoot, "packages", "next", "dist", "api.mjs"),
1835
1851
  "@farming-labs/next/changelog": join(workspaceRoot, "packages", "next", "dist", "changelog.mjs"),
package/dist/layout.mjs CHANGED
@@ -3,8 +3,75 @@ import { withNextApiReferenceBanner } from "./api-reference.mjs";
3
3
  import { emitDocsTelemetryProjectEvent } from "@farming-labs/docs";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { createDocsLayout, createDocsMetadata } from "@farming-labs/theme";
6
+ import { DocsCloudAnalytics } from "@farming-labs/docs/client/react";
6
7
 
7
8
  //#region src/layout.tsx
9
+ const DOCS_CLOUD_PROJECT_ID_ENVS = [
10
+ "NEXT_PUBLIC_DOCS_CLOUD_PROJECT_ID",
11
+ "PUBLIC_DOCS_CLOUD_PROJECT_ID",
12
+ "DOCS_CLOUD_PROJECT_ID"
13
+ ];
14
+ const DOCS_CLOUD_ANALYTICS_ENDPOINT_ENVS = [
15
+ "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT",
16
+ "PUBLIC_DOCS_CLOUD_ANALYTICS_ENDPOINT",
17
+ "DOCS_CLOUD_ANALYTICS_ENDPOINT"
18
+ ];
19
+ const DOCS_CLOUD_ANALYTICS_ROUTE_ENVS = [
20
+ "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ROUTE",
21
+ "PUBLIC_DOCS_CLOUD_ANALYTICS_ROUTE",
22
+ "DOCS_CLOUD_ANALYTICS_ROUTE"
23
+ ];
24
+ const DOCS_CLOUD_ANALYTICS_ENABLED_ENVS = [
25
+ "NEXT_PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED",
26
+ "PUBLIC_DOCS_CLOUD_ANALYTICS_ENABLED",
27
+ "DOCS_CLOUD_ANALYTICS_ENABLED"
28
+ ];
29
+ const DEFAULT_DOCS_CLOUD_ANALYTICS_ROUTE = "/api/docs?action=analytics";
30
+ function normalizeEnvValue(value) {
31
+ const normalized = value?.trim();
32
+ return normalized ? normalized : void 0;
33
+ }
34
+ function readFirstEnv(names) {
35
+ for (const name of names) {
36
+ const value = normalizeEnvValue(process.env[name]);
37
+ if (value) return value;
38
+ }
39
+ }
40
+ function isFalsyEnv(value) {
41
+ return /^(0|false|no|off)$/i.test(value ?? "");
42
+ }
43
+ function withAnalyticsAction(route) {
44
+ try {
45
+ const url = new URL(route, "https://docs.local");
46
+ if (!url.searchParams.has("action")) url.searchParams.set("action", "analytics");
47
+ return route.startsWith("http://") || route.startsWith("https://") ? url.toString() : `${url.pathname}${url.search}${url.hash}`;
48
+ } catch {
49
+ const [pathAndQuery, hash = ""] = route.split("#", 2);
50
+ return `${pathAndQuery}${pathAndQuery.includes("?") ? "&" : "?"}action=analytics${hash ? `#${hash}` : ""}`;
51
+ }
52
+ }
53
+ function normalizeRoute(value) {
54
+ const normalized = normalizeEnvValue(value);
55
+ if (!normalized) return void 0;
56
+ if (normalized.startsWith("http://") || normalized.startsWith("https://")) return withAnalyticsAction(normalized);
57
+ return withAnalyticsAction(`/${normalized}`.replace(/\/+/g, "/"));
58
+ }
59
+ function resolveNextDocsCloudClientOptions(config) {
60
+ const analytics = config.analytics;
61
+ if (analytics === false || analytics && typeof analytics === "object" && (analytics.enabled === false || analytics.cloud === false)) return false;
62
+ if (isFalsyEnv(readFirstEnv(DOCS_CLOUD_ANALYTICS_ENABLED_ENVS))) return false;
63
+ const projectId = readFirstEnv(DOCS_CLOUD_PROJECT_ID_ENVS);
64
+ if (!projectId) return void 0;
65
+ const endpoint = readFirstEnv(DOCS_CLOUD_ANALYTICS_ENDPOINT_ENVS);
66
+ const route = normalizeRoute(readFirstEnv(DOCS_CLOUD_ANALYTICS_ROUTE_ENVS)) ?? normalizeRoute(config.cloud?.apiRoute);
67
+ const includeInputs = typeof analytics === "object" && analytics.includeInputs === true;
68
+ return {
69
+ projectId,
70
+ endpoint: endpoint ?? route ?? DEFAULT_DOCS_CLOUD_ANALYTICS_ROUTE,
71
+ includeInputs,
72
+ metadata: { framework: "next" }
73
+ };
74
+ }
8
75
  function createNextDocsMetadata(config) {
9
76
  return createDocsMetadata(config);
10
77
  }
@@ -12,7 +79,12 @@ function createNextDocsLayout(config) {
12
79
  emitDocsTelemetryProjectEvent(config, { framework: "next" });
13
80
  const DocsLayout = createDocsLayout(withNextApiReferenceBanner(config));
14
81
  return function NextDocsLayout({ children }) {
15
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DocsClientCallbacks, {}), /* @__PURE__ */ jsx(DocsLayout, { children })] });
82
+ const docsCloud = resolveNextDocsCloudClientOptions(config);
83
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ docsCloud ? /* @__PURE__ */ jsx(DocsCloudAnalytics, { ...docsCloud }) : null,
85
+ /* @__PURE__ */ jsx(DocsClientCallbacks, { docsCloudEnabled: Boolean(docsCloud) }),
86
+ /* @__PURE__ */ jsx(DocsLayout, { children })
87
+ ] });
16
88
  };
17
89
  }
18
90
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.2.40",
3
+ "version": "0.2.42",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -106,8 +106,8 @@
106
106
  "tsdown": "^0.20.3",
107
107
  "typescript": "^5.9.3",
108
108
  "vitest": "^4.1.8",
109
- "@farming-labs/theme": "0.2.40",
110
- "@farming-labs/docs": "0.2.40"
109
+ "@farming-labs/docs": "0.2.42",
110
+ "@farming-labs/theme": "0.2.42"
111
111
  },
112
112
  "peerDependencies": {
113
113
  "@farming-labs/docs": ">=0.0.1",