@sungen/driver-api 3.1.2-beta.122 → 3.1.2-beta.124

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": "@sungen/driver-api",
3
- "version": "3.1.2-beta.122",
3
+ "version": "3.1.2-beta.124",
4
4
  "description": "Sungen API capability — the @api annotation, API catalog, OpenAPI import + `sungen api import`, and the specs/api.ts runtime template. Plugs into @sun-asterisk/sungen via the capability SPI.",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -13,7 +13,7 @@
13
13
  "author": "eqe team (engineer & quality) — Sun Asterisk",
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
- "@sun-asterisk/sungen": "3.1.2-beta.122",
16
+ "@sun-asterisk/sungen": "3.1.2-beta.124",
17
17
  "commander": "^14.0.2",
18
18
  "yaml": "^2.8.2"
19
19
  },
@@ -27,6 +27,7 @@ export interface ApiEntry {
27
27
  method: HttpMethod;
28
28
  path: string;
29
29
  body?: unknown;
30
+ encoding?: 'json' | 'form' | 'multipart'; // request-body wire format (default json); form = application/x-www-form-urlencoded
30
31
  headers?: Record<string, string>; // per-endpoint header templates; `:param` tokens bind at runtime (flow auth, e.g. authorization: "Bearer :token")
31
32
  params: string[];
32
33
  expect?: { status?: number | string };
@@ -89,6 +90,7 @@ function parseEntries(file: string, scope: 'screen' | 'shared'): Map<string, Api
89
90
  method: String(v.method).toUpperCase() as HttpMethod,
90
91
  path: v.path,
91
92
  body: v.body,
93
+ encoding: v.encoding === 'form' || v.encoding === 'multipart' ? v.encoding : undefined, // default (undefined) = json
92
94
  headers: v.headers && typeof v.headers === 'object' ? Object.fromEntries(Object.entries(v.headers).map(([k, hv]) => [k, String(hv)])) : undefined,
93
95
  params: Array.isArray(v.params) ? v.params.map((p: any) => String(p)) : [],
94
96
  expect: v.expect && typeof v.expect === 'object' ? { status: v.expect.status } : undefined,
package/src/index.ts CHANGED
@@ -85,7 +85,9 @@ function apiPreconditionCodegen(input: { tags: string[]; screenName: string; cwd
85
85
  // their `:param` placeholders bind at runtime from the same params — this is the flow auth/token
86
86
  // mechanism (the dynamic value comes via an override like `token={{login.body.token}}`).
87
87
  const hdr = entry.headers && Object.keys(entry.headers).length ? `, headers: ${JSON.stringify(entry.headers)}` : '';
88
- const req = `{ method: ${JSON.stringify(entry.method)}, path: ${JSON.stringify(entry.path)}${entry.body !== undefined ? `, body: ${JSON.stringify(entry.body)}` : ''}${hdr}, datasource: ${ds} }`;
88
+ // request-body wire format (default json); `form` x-www-form-urlencoded, `multipart` form-data (#345).
89
+ const enc = entry.encoding ? `, encoding: ${JSON.stringify(entry.encoding)}` : '';
90
+ const req = `{ method: ${JSON.stringify(entry.method)}, path: ${JSON.stringify(entry.path)}${entry.body !== undefined ? `, body: ${JSON.stringify(entry.body)}` : ''}${enc}${hdr}, datasource: ${ds} }`;
89
91
  // @concurrent:N → callN (N parallel calls, aggregate result); else a single call. @hybrid adds
90
92
  // the storageState opts so the request reuses the UI auth session.
91
93
  const callExpr = concurrent
@@ -14,6 +14,7 @@ export interface ImportedApiEntry {
14
14
  path: string;
15
15
  params?: string[];
16
16
  body?: Record<string, string>;
17
+ encoding?: 'form' | 'multipart'; // set when the requestBody is form/multipart (json omitted = default)
17
18
  expect?: { status: number };
18
19
  }
19
20
 
@@ -52,16 +53,24 @@ function expectedStatus(responses: Record<string, unknown> | undefined): number
52
53
  return success ?? codes[0];
53
54
  }
54
55
 
55
- /** JSON-body property names → a `{ prop: ":prop" }` template (best-effort from requestBody schema). */
56
- function bodyTemplate(op: any): { body?: Record<string, string>; props: string[] } {
57
- const schema = op?.requestBody?.content?.['application/json']?.schema;
56
+ /** Body property names → a `{ prop: ":prop" }` template, + the wire encoding (json | form | multipart),
57
+ * best-effort from the requestBody content-type (#345). JSON is the default (encoding omitted). */
58
+ function bodyTemplate(op: any): { body?: Record<string, string>; props: string[]; encoding?: 'form' | 'multipart' } {
59
+ const content = op?.requestBody?.content ?? {};
60
+ // Prefer JSON; else fall back to form-urlencoded / multipart (those set encoding).
61
+ const pick = content['application/json'] ? { ct: 'application/json' as const, encoding: undefined }
62
+ : content['application/x-www-form-urlencoded'] ? { ct: 'application/x-www-form-urlencoded' as const, encoding: 'form' as const }
63
+ : content['multipart/form-data'] ? { ct: 'multipart/form-data' as const, encoding: 'multipart' as const }
64
+ : undefined;
65
+ if (!pick) return { props: [] };
66
+ const schema = content[pick.ct]?.schema;
58
67
  const props = schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object'
59
68
  ? Object.keys(schema.properties)
60
69
  : [];
61
- if (!props.length) return { props: [] };
70
+ if (!props.length) return { props: [], encoding: pick.encoding };
62
71
  const body: Record<string, string> = {};
63
72
  for (const p of props) body[p] = `:${p}`;
64
- return { body, props };
73
+ return { body, props, encoding: pick.encoding };
65
74
  }
66
75
 
67
76
  export interface ImportResult {
@@ -89,7 +98,7 @@ export function importOpenApi(doc: any, datasource = 'app_api'): ImportResult {
89
98
 
90
99
  const catalogPath = toCatalogPath(rawPath);
91
100
  const pathParams = (catalogPath.match(/:([A-Za-z_][A-Za-z0-9_]*)/g) || []).map((s) => s.slice(1));
92
- const { body, props } = bodyTemplate(op);
101
+ const { body, props, encoding } = bodyTemplate(op);
93
102
  const params = [...new Set([...pathParams, ...props])];
94
103
 
95
104
  let name = typeof op.operationId === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(op.operationId)
@@ -107,6 +116,7 @@ export function importOpenApi(doc: any, datasource = 'app_api'): ImportResult {
107
116
  path: catalogPath,
108
117
  ...(params.length ? { params } : {}),
109
118
  ...(body ? { body } : {}),
119
+ ...(encoding ? { encoding } : {}),
110
120
  ...(status != null ? { expect: { status } } : {}),
111
121
  };
112
122
  }