@lukoweb/apitogo-module-landing 0.1.121 → 0.1.129

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-module-landing",
3
- "version": "0.1.121",
3
+ "version": "0.1.129",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.tsx"
@@ -14,7 +14,7 @@
14
14
  "devDependencies": {
15
15
  "@types/react": "19.2.14",
16
16
  "@types/react-dom": "19.2.3",
17
- "@lukoweb/apitogo": "0.1.121",
17
+ "@lukoweb/apitogo": "0.1.129",
18
18
  "react": "19.2.4",
19
19
  "react-dom": "19.2.4",
20
20
  "typescript": "5.9.3"
@@ -0,0 +1,131 @@
1
+ import type { ResolvedLandingContent } from "@lukoweb/apitogo";
2
+ import { useAuth } from "@lukoweb/apitogo/hooks";
3
+ import { type UseQueryResult, useQuery } from "@lukoweb/apitogo/react-query";
4
+
5
+ declare const VITE_APITOGO_AUTHENTICATION_API_BASE_URL: string | undefined;
6
+ declare const VITE_APITOGO_DEV_PORTAL_API_URL: string | undefined;
7
+
8
+ type ApiShowcaseItem = NonNullable<
9
+ NonNullable<ResolvedLandingContent["apiShowcase"]>["items"]
10
+ >[number];
11
+
12
+ const PLACEHOLDER_HOST = "dp-example.azurecontainerapps.io";
13
+
14
+ /** One operation of your API, as synced by the dev portal backend from the gateway. */
15
+ export type GatewayAction = {
16
+ /** Stable key, e.g. `get:/orders/{id}`. */
17
+ actionKey: string;
18
+ /** Upper-case HTTP method, e.g. `GET`. */
19
+ method: string;
20
+ /** Path template, e.g. `/orders/{id}`. */
21
+ path: string;
22
+ /** True when the action can be called without an API key. */
23
+ isPublic: boolean;
24
+ };
25
+
26
+ export type GatewayActionCatalog = {
27
+ actions: GatewayAction[];
28
+ lastSyncedAtUtc: string | null;
29
+ };
30
+
31
+ /** Server-side filters for `GET /api/v1/actions`. All filters are combined with AND. */
32
+ export type GatewayActionFilters = {
33
+ /** Only actions included in at least one active plan. */
34
+ inPlan?: boolean;
35
+ /** Only actions included in this plan SKU. */
36
+ plan?: string;
37
+ isPublic?: boolean;
38
+ method?: string;
39
+ pathPrefix?: string;
40
+ /** Max number of actions (server caps at 500). */
41
+ limit?: number;
42
+ };
43
+
44
+ export type UseGatewayActionsOptions = GatewayActionFilters & {
45
+ /** Set to false to skip the request (e.g. behind a feature flag). */
46
+ enabled?: boolean;
47
+ };
48
+
49
+ type DevPortalProviderData = { type?: string; apiBaseUrl?: string };
50
+
51
+ const trimUrl = (url: string | undefined) =>
52
+ url?.trim().replace(/\/+$/, "") || undefined;
53
+
54
+ const resolveEnvApiBaseUrl = () =>
55
+ trimUrl(
56
+ (typeof VITE_APITOGO_AUTHENTICATION_API_BASE_URL === "string"
57
+ ? VITE_APITOGO_AUTHENTICATION_API_BASE_URL
58
+ : undefined) ||
59
+ (typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string"
60
+ ? VITE_APITOGO_DEV_PORTAL_API_URL
61
+ : undefined),
62
+ );
63
+
64
+ const toSearchParams = (filters: GatewayActionFilters) =>
65
+ new URLSearchParams(
66
+ Object.entries(filters).flatMap(([key, value]) =>
67
+ value === undefined || value === "" ? [] : [[key, String(value)]],
68
+ ),
69
+ );
70
+
71
+ /**
72
+ * Loads your API's gateway actions from the dev portal backend (`GET /api/v1/actions`).
73
+ *
74
+ * Runs in the browser only, so prerendered pages never bake in a stale list. Disabled in
75
+ * preview/demo mode or when no dev portal backend URL is configured — render a fallback
76
+ * list while `data` is undefined.
77
+ */
78
+ export const useGatewayActions = ({
79
+ enabled = true,
80
+ ...filters
81
+ }: UseGatewayActionsOptions = {}): UseQueryResult<GatewayActionCatalog> => {
82
+ const auth = useAuth();
83
+ const providerData = auth.providerData as DevPortalProviderData | null;
84
+ const apiBaseUrl =
85
+ (providerData?.type === "dev-portal"
86
+ ? trimUrl(providerData.apiBaseUrl)
87
+ : undefined) ?? resolveEnvApiBaseUrl();
88
+
89
+ const isLive =
90
+ Boolean(apiBaseUrl) &&
91
+ !apiBaseUrl?.includes(PLACEHOLDER_HOST) &&
92
+ auth.authMode !== "preview" &&
93
+ auth.isBackendAvailable !== false &&
94
+ auth.isDemoSession !== true;
95
+
96
+ const search = toSearchParams(filters).toString();
97
+
98
+ return useQuery<GatewayActionCatalog>({
99
+ queryKey: ["dev-portal-gateway-actions", apiBaseUrl, search],
100
+ enabled: enabled && isLive,
101
+ staleTime: 5 * 60 * 1000,
102
+ queryFn: async ({ signal }) => {
103
+ const response = await fetch(
104
+ `${apiBaseUrl}/api/v1/actions${search ? `?${search}` : ""}`,
105
+ { headers: { Accept: "application/json" }, signal },
106
+ );
107
+
108
+ if (!response.ok) {
109
+ throw new Error("Failed to load API actions.");
110
+ }
111
+
112
+ const body = (await response.json()) as Partial<GatewayActionCatalog>;
113
+ return {
114
+ actions: Array.isArray(body.actions) ? body.actions : [],
115
+ lastSyncedAtUtc: body.lastSyncedAtUtc ?? null,
116
+ };
117
+ },
118
+ });
119
+ };
120
+
121
+ /** Maps gateway actions to `ApiShowcaseSection` items. Pass `summaries` to label actions by key. */
122
+ export const toApiShowcaseItems = (
123
+ actions: GatewayAction[],
124
+ summaries: Record<string, string> = {},
125
+ ): ApiShowcaseItem[] =>
126
+ actions.map((action) => ({
127
+ name: summaries[action.actionKey] ?? `${action.method} ${action.path}`,
128
+ method: action.method,
129
+ path: action.path,
130
+ summary: summaries[action.actionKey],
131
+ }));
package/src/index.tsx CHANGED
@@ -30,3 +30,13 @@ export { FooterSection } from "./sections/FooterSection.js";
30
30
  export type { FooterSectionProps } from "./sections/FooterSection.js";
31
31
  export { CodeExampleCard } from "./sections/CodeExampleCard.js";
32
32
  export type { CodeExampleCardProps } from "./sections/CodeExampleCard.js";
33
+ export {
34
+ toApiShowcaseItems,
35
+ useGatewayActions,
36
+ } from "./hooks/useGatewayActions.js";
37
+ export type {
38
+ GatewayAction,
39
+ GatewayActionCatalog,
40
+ GatewayActionFilters,
41
+ UseGatewayActionsOptions,
42
+ } from "./hooks/useGatewayActions.js";
@@ -29,7 +29,9 @@ export const StepsSection = ({
29
29
  <div className="font-mono text-[2rem] font-bold text-primary">
30
30
  {item.step}
31
31
  </div>
32
- <h3 className="mt-2 mb-1.5 text-lg font-semibold">{item.title}</h3>
32
+ <h3 className="mt-2 mb-1.5 text-lg font-semibold">
33
+ {item.title}
34
+ </h3>
33
35
  <p className="text-sm leading-relaxed text-muted-foreground">
34
36
  {item.description}
35
37
  </p>