@lukoweb/apitogo 0.2.50 → 0.2.51

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/cli/cli.js CHANGED
@@ -6268,7 +6268,7 @@ title: Welcome
6268
6268
 
6269
6269
  # ${name}
6270
6270
 
6271
- This page was created by \`apitogo init\`. Edit it, and add more \`.md\` or \`.mdx\` files next to it: each one becomes a page of your portal.
6271
+ Welcome to the ${name} developer portal. Here you will find guides, the API reference, and everything you need to get an API key and make your first call.
6272
6272
  `;
6273
6273
  }
6274
6274
  async function docsFromPaths(dir, requested) {
@@ -7657,7 +7657,8 @@ async function removeServicesFromManifest(state, names) {
7657
7657
  // src/config/v2/gateway-reference.ts
7658
7658
  import { readFile as readFile6 } from "node:fs/promises";
7659
7659
  import { parse } from "yaml";
7660
- var DEFAULT_KEY_HEADER = "Ocp-Apim-Subscription-Key";
7660
+ var DEFAULT_KEY_HEADER = "X-API-Key";
7661
+ var KEY_HEADER_EXTENSION = "x-apitogo-key-header";
7661
7662
  var METHODS = [
7662
7663
  "get",
7663
7664
  "put",
@@ -7770,10 +7771,13 @@ function buildReferenceSpec(specText, options) {
7770
7771
  if (Object.keys(components).length > 0) doc.components = components;
7771
7772
  else delete doc.components;
7772
7773
  doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
7773
- for (const [, item, method] of operationsOf(paths)) {
7774
+ for (const [path97, item, method] of operationsOf(paths)) {
7774
7775
  const operation = item[method];
7775
7776
  if (Array.isArray(operation.security) && operation.security.length > 0)
7776
7777
  delete operation.security;
7778
+ if (options.apiKeyRequired && !options.publicOperations.has(actionKey(method, path97)))
7779
+ operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
7780
+ else delete operation[KEY_HEADER_EXTENSION];
7777
7781
  }
7778
7782
  return JSON.parse(hideOrigin(JSON.stringify(doc), options));
7779
7783
  }
@@ -8089,6 +8093,8 @@ function planPortal(profile, manifest) {
8089
8093
  apiBaseUrl: plans.apiBaseUrl,
8090
8094
  keysApiBaseUrl: plans.keysApiBaseUrl,
8091
8095
  billing: plans.billing,
8096
+ ...gateway?.keyHeaderName ? { keyHeader: gateway.keyHeaderName } : {},
8097
+ ...gateway?.baseUrl && serviceReady(gateway) ? { gatewayBaseUrl: gateway.baseUrl } : {},
8092
8098
  links: {
8093
8099
  // Hosted docs are linked once their pages are known (see `buildPortalWorkspace`): there is no page to
8094
8100
  // name before that, and a guessed one was a 404 on every portal whose first page is not called `introduction`.
@@ -8630,7 +8636,7 @@ var FULL_SPEC = (name) => `openapi: 3.1.0
8630
8636
  info:
8631
8637
  title: ${JSON.stringify(name)}
8632
8638
  version: 1.0.0
8633
- description: A small sample API. Edit server/index.js and this file together.
8639
+ description: A sample API with a greeting and a list of items.
8634
8640
  paths:
8635
8641
  /health:
8636
8642
  get:
@@ -8719,10 +8725,14 @@ components:
8719
8725
  // src/config/v2/starter-landing.ts
8720
8726
  function starterLandingPage(options) {
8721
8727
  const name = JSON.stringify(options.name);
8722
- const plansImport = options.withPlans ? 'import { usePlansPreview } from "@lukoweb/apitogo-plugin-dev-portal-billing";\n' : "";
8723
- const plansHook = options.withPlans ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n\n" : "";
8728
+ const plansImport = options.withPlans ? 'import {\n useGatewayCall,\n usePlansPreview,\n} from "@lukoweb/apitogo-plugin-dev-portal-billing";\n' : "";
8729
+ const plansHook = options.withPlans ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n // The real gateway address and key header, so the sample works when it is pasted with a key.\n const gateway = useGatewayCall();\n\n" : "";
8730
+ const sampleUrl = options.withPlans ? '${gateway.baseUrl ?? "https://YOUR-GATEWAY"}' : "https://YOUR-GATEWAY";
8731
+ const sampleHeader = options.withPlans ? "${gateway.keyHeader}" : "X-API-Key";
8724
8732
  const plansSection = options.withPlans ? " <PlansPreviewSection plans={plans} />\n" : "";
8725
- return `import {
8733
+ return `// Your portal's home page. Edit this file (apitogo/portal/landing/LandingPage.tsx) to make it yours: everything
8734
+ // on it is public, so keep notes for yourself in comments like this one.
8735
+ import {
8726
8736
  ApiShowcaseSection,
8727
8737
  FeaturesSection,
8728
8738
  HeroSection,
@@ -8752,8 +8762,8 @@ ${plansHook} return (
8752
8762
  {
8753
8763
  filename: "Call the API",
8754
8764
  language: "bash",
8755
- code: \`$ curl https://your-api.example.com/health \\\\
8756
- -H "x-api-key: YOUR_API_KEY"
8765
+ code: \`$ curl ${sampleUrl}/health \\\\
8766
+ -H "${sampleHeader}: YOUR_API_KEY"
8757
8767
  # { "status": "ok" }\`,
8758
8768
  },
8759
8769
  ]}
@@ -8763,7 +8773,7 @@ ${plansHook} return (
8763
8773
  label: "What you get",
8764
8774
  title: "A developer portal that is ready on day one",
8765
8775
  description:
8766
- "Edit this page in apitogo/portal/landing/LandingPage.tsx to make it yours.",
8776
+ "Docs, a live API reference, self-service API keys and clear plans, all in one place.",
8767
8777
  }}
8768
8778
  features={[
8769
8779
  {
@@ -10771,7 +10781,7 @@ import {
10771
10781
  // package.json
10772
10782
  var package_default = {
10773
10783
  name: "@lukoweb/apitogo",
10774
- version: "0.2.50",
10784
+ version: "0.2.51",
10775
10785
  type: "module",
10776
10786
  sideEffects: [
10777
10787
  "**/*.css",
@@ -16080,7 +16090,7 @@ async function onlinePlansRules(resolved, projectId, api) {
16080
16090
  const convert = (item) => issue3(
16081
16091
  "PLANS_PLAN_INVALID",
16082
16092
  `plans.file ${item.field}`,
16083
- `${item.sku ? `plan ${item.sku}: ` : ""}${item.message}`
16093
+ withPlanName(item.message, item.sku)
16084
16094
  );
16085
16095
  return {
16086
16096
  errors: answer.errors.map(convert),
@@ -16094,6 +16104,7 @@ async function onlinePlansRules(resolved, projectId, api) {
16094
16104
  };
16095
16105
  }
16096
16106
  }
16107
+ var withPlanName = (message, sku) => !sku || message.toLowerCase().startsWith(`plan "${sku.toLowerCase()}"`) ? message : `plan ${sku}: ${message}`;
16097
16108
 
16098
16109
  // src/cli/check/v2.ts
16099
16110
  async function isConfigV2(dir) {
@@ -19096,10 +19107,13 @@ export default function ${pageComponentName(slug)}() {
19096
19107
  <Typography>
19097
19108
  <Heading level={1}>{title}</Heading>
19098
19109
  <p>${text}</p>
19099
- <Callout type="info" title="Where this page lives">
19100
- Edit <code>${file}</code>. Files whose name starts with <code>_</code> are not pages, so you can keep shared
19101
- components next to your pages.
19102
- </Callout>
19110
+ {/* A note for you while you run the portal locally (\`apitogo dev\`); the deployed portal does not show it. */}
19111
+ {import.meta.env.DEV && (
19112
+ <Callout type="info" title="Where this page lives">
19113
+ Edit <code>${file}</code>. Files whose name starts with <code>_</code> are not pages, so you can keep
19114
+ shared components next to your pages.
19115
+ </Callout>
19116
+ )}
19103
19117
  </Typography>
19104
19118
  </section>
19105
19119
  );
@@ -21330,7 +21344,7 @@ function buildPostPublishChecklist(args) {
21330
21344
  if (args.gatewayUrl) {
21331
21345
  items.push({
21332
21346
  id: "test_gateway",
21333
- label: "Call the API gateway with Ocp-Apim-Subscription-Key",
21347
+ label: "Call the API gateway with X-API-Key",
21334
21348
  required: false,
21335
21349
  completed: false,
21336
21350
  url: args.gatewayUrl
@@ -26619,7 +26633,9 @@ async function deployGateway(resolved, projectId, deps, options = {}) {
26619
26633
  baseUrl,
26620
26634
  capabilities: capabilities.capabilities,
26621
26635
  openapiUrl: status2.openApiPath ?? void 0,
26622
- openapiHash: status2.openApiHash ?? status2.specSha256 ?? void 0
26636
+ openapiHash: status2.openApiHash ?? status2.specSha256 ?? void 0,
26637
+ // The portal's keys page and API page show this header (API-120).
26638
+ keyHeaderName: resolved.gateway?.policies.access?.apiKeyHeader ?? DEFAULT_KEY_HEADER
26623
26639
  })
26624
26640
  );
26625
26641
  }
@@ -1,5 +1,7 @@
1
1
  import { HTTPSnippet } from "@zudoku/httpsnippet";
2
2
  import type { OperationsFragmentFragment } from "../graphql/graphql.js";
3
+ export declare const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
4
+ export declare const KEY_PLACEHOLDER = "YOUR_API_KEY";
3
5
  export declare const createHttpSnippet: ({ operation, selectedServer, exampleBody, }: {
4
6
  operation: OperationsFragmentFragment;
5
7
  selectedServer: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.2.50",
3
+ "version": "0.2.51",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -105,7 +105,7 @@ export const FULL_SPEC = (name: string) => `openapi: 3.1.0
105
105
  info:
106
106
  title: ${JSON.stringify(name)}
107
107
  version: 1.0.0
108
- description: A small sample API. Edit server/index.js and this file together.
108
+ description: A sample API with a greeting and a list of items.
109
109
  paths:
110
110
  /health:
111
111
  get:
@@ -18,7 +18,13 @@ export type ReferenceOptions = {
18
18
  };
19
19
 
20
20
  /** Header of the API key when the gateway does not say otherwise (same as the platform's OpenAPI export). */
21
- export const DEFAULT_KEY_HEADER = "Ocp-Apim-Subscription-Key";
21
+ export const DEFAULT_KEY_HEADER = "X-API-Key";
22
+
23
+ /**
24
+ * Operation extension naming the header a protected operation's key goes in. The API page's code samples read it
25
+ * (`createHttpSnippet`), so they show the header with a placeholder key.
26
+ */
27
+ export const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
22
28
 
23
29
  const METHODS = [
24
30
  "get",
@@ -175,10 +181,17 @@ export function buildReferenceSpec(
175
181
  else delete doc.components;
176
182
  // One requirement with both schemes means both are needed together.
177
183
  doc.security = Object.keys(requirement).length > 0 ? [requirement] : [];
178
- for (const [, item, method] of operationsOf(paths)) {
184
+ for (const [path, item, method] of operationsOf(paths)) {
179
185
  const operation = item[method] as Json;
180
186
  if (Array.isArray(operation.security) && operation.security.length > 0)
181
187
  delete operation.security;
188
+ // The page's code samples add this header, so a copied sample works once the key is filled in (API-120).
189
+ if (
190
+ options.apiKeyRequired &&
191
+ !options.publicOperations.has(actionKey(method, path))
192
+ )
193
+ operation[KEY_HEADER_EXTENSION] = options.apiKeyHeader;
194
+ else delete operation[KEY_HEADER_EXTENSION];
182
195
  }
183
196
 
184
197
  return JSON.parse(hideOrigin(JSON.stringify(doc), options)) as Json;
@@ -59,7 +59,7 @@ title: Welcome
59
59
 
60
60
  # ${name}
61
61
 
62
- This page was created by \`apitogo init\`. Edit it, and add more \`.md\` or \`.mdx\` files next to it: each one becomes a page of your portal.
62
+ Welcome to the ${name} developer portal. Here you will find guides, the API reference, and everything you need to get an API key and make your first call.
63
63
  `;
64
64
  }
65
65
 
@@ -9,15 +9,22 @@ export function starterLandingPage(options: {
9
9
  }): string {
10
10
  const name = JSON.stringify(options.name);
11
11
  const plansImport = options.withPlans
12
- ? 'import { usePlansPreview } from "@lukoweb/apitogo-plugin-dev-portal-billing";\n'
12
+ ? 'import {\n useGatewayCall,\n usePlansPreview,\n} from "@lukoweb/apitogo-plugin-dev-portal-billing";\n'
13
13
  : "";
14
14
  const plansHook = options.withPlans
15
- ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n\n"
15
+ ? " // The plans of the project's catalog: the same ones the pricing page shows.\n const plans = usePlansPreview();\n // The real gateway address and key header, so the sample works when it is pasted with a key.\n const gateway = useGatewayCall();\n\n"
16
16
  : "";
17
+ // Without the plans service the page cannot learn the gateway, so the sample names the platform's default header.
18
+ const sampleUrl = options.withPlans
19
+ ? '${gateway.baseUrl ?? "https://YOUR-GATEWAY"}'
20
+ : "https://YOUR-GATEWAY";
21
+ const sampleHeader = options.withPlans ? "${gateway.keyHeader}" : "X-API-Key";
17
22
  const plansSection = options.withPlans
18
23
  ? " <PlansPreviewSection plans={plans} />\n"
19
24
  : "";
20
- return `import {
25
+ return `// Your portal's home page. Edit this file (apitogo/portal/landing/LandingPage.tsx) to make it yours: everything
26
+ // on it is public, so keep notes for yourself in comments like this one.
27
+ import {
21
28
  ApiShowcaseSection,
22
29
  FeaturesSection,
23
30
  HeroSection,
@@ -47,8 +54,8 @@ ${plansHook} return (
47
54
  {
48
55
  filename: "Call the API",
49
56
  language: "bash",
50
- code: \`$ curl https://your-api.example.com/health \\\\
51
- -H "x-api-key: YOUR_API_KEY"
57
+ code: \`$ curl ${sampleUrl}/health \\\\
58
+ -H "${sampleHeader}: YOUR_API_KEY"
52
59
  # { "status": "ok" }\`,
53
60
  },
54
61
  ]}
@@ -58,7 +65,7 @@ ${plansHook} return (
58
65
  label: "What you get",
59
66
  title: "A developer portal that is ready on day one",
60
67
  description:
61
- "Edit this page in apitogo/portal/landing/LandingPage.tsx to make it yours.",
68
+ "Docs, a live API reference, self-service API keys and clear plans, all in one place.",
62
69
  }}
63
70
  features={[
64
71
  {
@@ -77,6 +77,10 @@ export type PortalPlan = {
77
77
  billing?: string;
78
78
  /** Where the account pages link to: only places this portal has (hosted docs, an external docs site, the API page). */
79
79
  links?: { docs?: string; api?: string };
80
+ /** The header API users send their key in, from the gateway entry of services.json (API-120). */
81
+ keyHeader?: string;
82
+ /** The gateway's address, for code samples on the portal's pages. */
83
+ gatewayBaseUrl?: string;
80
84
  };
81
85
  /** Account pages. */
82
86
  userPanel: boolean;
@@ -259,6 +263,10 @@ export function planPortal(
259
263
  apiBaseUrl: plans.apiBaseUrl,
260
264
  keysApiBaseUrl: plans.keysApiBaseUrl,
261
265
  billing: plans.billing,
266
+ ...(gateway?.keyHeaderName ? { keyHeader: gateway.keyHeaderName } : {}),
267
+ ...(gateway?.baseUrl && serviceReady(gateway)
268
+ ? { gatewayBaseUrl: gateway.baseUrl }
269
+ : {}),
262
270
  links: {
263
271
  // Hosted docs are linked once their pages are known (see `buildPortalWorkspace`): there is no page to
264
272
  // name before that, and a guessed one was a 404 on every portal whose first page is not called `introduction`.
@@ -1,135 +1,147 @@
1
- import { HTTPSnippet } from "@zudoku/httpsnippet";
2
- import type { OperationsFragmentFragment } from "../graphql/graphql.js";
3
-
4
- const toFormDataParams = (text?: string) => {
5
- const stringify = (v: unknown) =>
6
- typeof v === "string" ? v : JSON.stringify(v);
7
-
8
- try {
9
- const obj = text && JSON.parse(text);
10
- if (typeof obj !== "object" || !obj) return [];
11
-
12
- return Object.entries(obj).flatMap(([name, value]) => {
13
- const values = Array.isArray(value) ? value : [value];
14
- return values.map((v) => ({ name, value: stringify(v) }));
15
- });
16
- } catch {
17
- return [];
18
- }
19
- };
20
-
21
- export const createHttpSnippet = ({
22
- operation,
23
- selectedServer,
24
- exampleBody,
25
- }: {
26
- operation: OperationsFragmentFragment;
27
- selectedServer: string;
28
- exampleBody: {
29
- mimeType: string;
30
- text?: string;
31
- };
32
- }) => {
33
- const isMultipart =
34
- exampleBody.mimeType === "multipart/form-data" ||
35
- exampleBody.mimeType === "application/x-www-form-urlencoded";
36
-
37
- const postData = isMultipart
38
- ? {
39
- mimeType: exampleBody.mimeType,
40
- params: toFormDataParams(exampleBody.text),
41
- }
42
- : exampleBody;
43
-
44
- return new HTTPSnippet({
45
- method: operation.method.toUpperCase(),
46
- url:
47
- selectedServer + operation.path.replaceAll("{", ":").replaceAll("}", ""),
48
- postData,
49
- headers: [
50
- ...(exampleBody.text
51
- ? [{ name: "Content-Type", value: exampleBody.mimeType }]
52
- : []),
53
- ...(operation.parameters
54
- ?.filter((p) => p.in === "header" && p.required === true)
55
- .map((p) => ({
56
- name: p.name,
57
- value:
58
- p.schema?.default ??
59
- p.examples?.find((x) => x.value)?.value ??
60
- (p.schema?.type === "string"
61
- ? "<string>"
62
- : p.schema?.type === "number" || p.schema?.type === "integer"
63
- ? "<number>"
64
- : p.schema?.type === "boolean"
65
- ? "<bool>"
66
- : "<value>"),
67
- })) ?? []),
68
- ],
69
- queryString:
70
- operation.parameters
71
- ?.filter((p) => p.in === "query" && p.required === true)
72
- .map((p) => ({
73
- name: p.name,
74
- value:
75
- p.schema?.default ??
76
- p.examples?.find((x) => x.value)?.value ??
77
- (p.schema?.type === "string"
78
- ? "<string>"
79
- : p.schema?.type === "number" || p.schema?.type === "integer"
80
- ? "<number>"
81
- : p.schema?.type === "boolean"
82
- ? "<bool>"
83
- : "<value>"),
84
- })) ?? [],
85
- httpVersion: "",
86
- cookies: [],
87
- headersSize: 0,
88
- bodySize: 0,
89
- });
90
- };
91
-
92
- export const getConverted = (snippet: HTTPSnippet, option: string) => {
93
- // biome-ignore lint/suspicious/noExplicitAny: Allow any type
94
- let converted: any;
95
- switch (option) {
96
- case "shell":
97
- converted = snippet.convert("shell", "curl");
98
- break;
99
- case "js":
100
- converted = snippet.convert("javascript", "fetch");
101
- break;
102
- case "python":
103
- converted = snippet.convert("python", "requests");
104
- break;
105
- case "java":
106
- converted = snippet.convert("java", "okhttp");
107
- break;
108
- case "go":
109
- converted = snippet.convert("go", "native");
110
- break;
111
- case "csharp":
112
- converted = snippet.convert("csharp", "httpclient");
113
- break;
114
- case "kotlin":
115
- converted = snippet.convert("kotlin", "okhttp");
116
- break;
117
- case "objc":
118
- converted = snippet.convert("objc", "nsurlsession");
119
- break;
120
- case "php":
121
- converted = snippet.convert("php", "http2");
122
- break;
123
- case "ruby":
124
- converted = snippet.convert("ruby");
125
- break;
126
- case "swift":
127
- converted = snippet.convert("swift");
128
- break;
129
- default:
130
- converted = snippet.convert("shell");
131
- break;
132
- }
133
-
134
- return converted ? converted[0] : "";
135
- };
1
+ import { HTTPSnippet } from "@zudoku/httpsnippet";
2
+ import type { OperationsFragmentFragment } from "../graphql/graphql.js";
3
+
4
+ /** Operation extension with the header a key goes in (same name as `KEY_HEADER_EXTENSION` in the CLI's config). */
5
+ export const KEY_HEADER_EXTENSION = "x-apitogo-key-header";
6
+
7
+ /** What a code sample shows where the visitor's key goes. */
8
+ export const KEY_PLACEHOLDER = "YOUR_API_KEY";
9
+
10
+ const toFormDataParams = (text?: string) => {
11
+ const stringify = (v: unknown) =>
12
+ typeof v === "string" ? v : JSON.stringify(v);
13
+
14
+ try {
15
+ const obj = text && JSON.parse(text);
16
+ if (typeof obj !== "object" || !obj) return [];
17
+
18
+ return Object.entries(obj).flatMap(([name, value]) => {
19
+ const values = Array.isArray(value) ? value : [value];
20
+ return values.map((v) => ({ name, value: stringify(v) }));
21
+ });
22
+ } catch {
23
+ return [];
24
+ }
25
+ };
26
+
27
+ export const createHttpSnippet = ({
28
+ operation,
29
+ selectedServer,
30
+ exampleBody,
31
+ }: {
32
+ operation: OperationsFragmentFragment;
33
+ selectedServer: string;
34
+ exampleBody: {
35
+ mimeType: string;
36
+ text?: string;
37
+ };
38
+ }) => {
39
+ const isMultipart =
40
+ exampleBody.mimeType === "multipart/form-data" ||
41
+ exampleBody.mimeType === "application/x-www-form-urlencoded";
42
+
43
+ const postData = isMultipart
44
+ ? {
45
+ mimeType: exampleBody.mimeType,
46
+ params: toFormDataParams(exampleBody.text),
47
+ }
48
+ : exampleBody;
49
+
50
+ const keyHeader = operation.extensions?.[KEY_HEADER_EXTENSION];
51
+
52
+ return new HTTPSnippet({
53
+ method: operation.method.toUpperCase(),
54
+ url:
55
+ selectedServer + operation.path.replaceAll("{", ":").replaceAll("}", ""),
56
+ postData,
57
+ headers: [
58
+ // The gateway's key header, on operations that need a key (set by the portal build, API-120).
59
+ ...(typeof keyHeader === "string" && keyHeader
60
+ ? [{ name: keyHeader, value: KEY_PLACEHOLDER }]
61
+ : []),
62
+ ...(exampleBody.text
63
+ ? [{ name: "Content-Type", value: exampleBody.mimeType }]
64
+ : []),
65
+ ...(operation.parameters
66
+ ?.filter((p) => p.in === "header" && p.required === true)
67
+ .map((p) => ({
68
+ name: p.name,
69
+ value:
70
+ p.schema?.default ??
71
+ p.examples?.find((x) => x.value)?.value ??
72
+ (p.schema?.type === "string"
73
+ ? "<string>"
74
+ : p.schema?.type === "number" || p.schema?.type === "integer"
75
+ ? "<number>"
76
+ : p.schema?.type === "boolean"
77
+ ? "<bool>"
78
+ : "<value>"),
79
+ })) ?? []),
80
+ ],
81
+ queryString:
82
+ operation.parameters
83
+ ?.filter((p) => p.in === "query" && p.required === true)
84
+ .map((p) => ({
85
+ name: p.name,
86
+ value:
87
+ p.schema?.default ??
88
+ p.examples?.find((x) => x.value)?.value ??
89
+ (p.schema?.type === "string"
90
+ ? "<string>"
91
+ : p.schema?.type === "number" || p.schema?.type === "integer"
92
+ ? "<number>"
93
+ : p.schema?.type === "boolean"
94
+ ? "<bool>"
95
+ : "<value>"),
96
+ })) ?? [],
97
+ httpVersion: "",
98
+ cookies: [],
99
+ headersSize: 0,
100
+ bodySize: 0,
101
+ });
102
+ };
103
+
104
+ export const getConverted = (snippet: HTTPSnippet, option: string) => {
105
+ // biome-ignore lint/suspicious/noExplicitAny: Allow any type
106
+ let converted: any;
107
+ switch (option) {
108
+ case "shell":
109
+ converted = snippet.convert("shell", "curl");
110
+ break;
111
+ case "js":
112
+ converted = snippet.convert("javascript", "fetch");
113
+ break;
114
+ case "python":
115
+ converted = snippet.convert("python", "requests");
116
+ break;
117
+ case "java":
118
+ converted = snippet.convert("java", "okhttp");
119
+ break;
120
+ case "go":
121
+ converted = snippet.convert("go", "native");
122
+ break;
123
+ case "csharp":
124
+ converted = snippet.convert("csharp", "httpclient");
125
+ break;
126
+ case "kotlin":
127
+ converted = snippet.convert("kotlin", "okhttp");
128
+ break;
129
+ case "objc":
130
+ converted = snippet.convert("objc", "nsurlsession");
131
+ break;
132
+ case "php":
133
+ converted = snippet.convert("php", "http2");
134
+ break;
135
+ case "ruby":
136
+ converted = snippet.convert("ruby");
137
+ break;
138
+ case "swift":
139
+ converted = snippet.convert("swift");
140
+ break;
141
+ default:
142
+ converted = snippet.convert("shell");
143
+ break;
144
+ }
145
+
146
+ return converted ? converted[0] : "";
147
+ };