@api-client/core 0.6.0 → 0.6.3

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 (32) hide show
  1. package/build/browser.d.ts +0 -1
  2. package/build/browser.js +0 -1
  3. package/build/browser.js.map +1 -1
  4. package/build/index.d.ts +0 -1
  5. package/build/index.js +0 -1
  6. package/build/index.js.map +1 -1
  7. package/build/src/lib/transformers/PayloadSerializer.d.ts +79 -16
  8. package/build/src/lib/transformers/PayloadSerializer.js +167 -61
  9. package/build/src/lib/transformers/PayloadSerializer.js.map +1 -1
  10. package/build/src/mocking/lib/Request.js +1 -1
  11. package/build/src/mocking/lib/Request.js.map +1 -1
  12. package/build/src/mocking/lib/Response.js +5 -2
  13. package/build/src/mocking/lib/Response.js.map +1 -1
  14. package/build/src/runtime/http-engine/CoreEngine.js +3 -6
  15. package/build/src/runtime/http-engine/CoreEngine.js.map +1 -1
  16. package/build/src/runtime/http-engine/FormData.d.ts +36 -2
  17. package/build/src/runtime/http-engine/FormData.js +156 -55
  18. package/build/src/runtime/http-engine/FormData.js.map +1 -1
  19. package/build/src/runtime/http-engine/PayloadSupport.d.ts +4 -5
  20. package/build/src/runtime/http-engine/PayloadSupport.js +19 -12
  21. package/build/src/runtime/http-engine/PayloadSupport.js.map +1 -1
  22. package/package.json +1 -3
  23. package/src/lib/transformers/PayloadSerializer.ts +206 -70
  24. package/src/mocking/lib/Request.ts +1 -1
  25. package/src/mocking/lib/Response.ts +5 -2
  26. package/src/runtime/http-engine/CoreEngine.ts +3 -6
  27. package/src/runtime/http-engine/FormData.ts +184 -63
  28. package/src/runtime/http-engine/PayloadSupport.ts +23 -15
  29. package/build/src/lib/transformers/Utils.d.ts +0 -7
  30. package/build/src/lib/transformers/Utils.js +0 -19
  31. package/build/src/lib/transformers/Utils.js.map +0 -1
  32. package/src/lib/transformers/Utils.ts +0 -18
@@ -1,85 +1,206 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
2
- import _FormData from 'form-data';
3
- import { IMultipartBody, PayloadSerializer } from '../../lib/transformers/PayloadSerializer.js';
1
+ import { IMultipartBody, PayloadSerializer, IBlobMeta, IFileMeta } from '../../lib/transformers/PayloadSerializer.js';
4
2
 
5
- let target: _FormData;
3
+ export interface FormDataResult {
4
+ /**
5
+ * The contents of the form data
6
+ */
7
+ buffer: Buffer;
8
+ /**
9
+ * Content type for the form data.
10
+ */
11
+ type: string;
12
+ }
13
+
14
+ interface IPart {
15
+ name: string;
16
+ type: 'file' | 'string';
17
+ meta?: IBlobMeta | IFileMeta;
18
+ contents: Buffer;
19
+ }
20
+
21
+ const DefaultContentType = 'application/octet-stream';
22
+ const LineBreak = '\r\n';
6
23
 
7
24
  /**
8
- * Adds a part to the form.
25
+ * In API Client (old old version of ARC) multipart data is encoded
26
+ * as serializable array of buffer views. This class transforms these data
27
+ * into a valid form data contents as a Buffer and generates content type header with the proper boundary.
9
28
  */
10
- async function _append(part: IMultipartBody): Promise<void> {
11
- const { name, value, isFile, type, fileName, enabled } = part;
12
- if (enabled === false) {
13
- return;
14
- }
29
+ export class FormDataNode {
30
+ protected _boundary?: string;
15
31
 
16
- let blob;
17
- if (isFile) {
18
- blob = PayloadSerializer.deserializeBlobBuffer(value);
19
- if (blob) {
20
- target.append(name, blob, {
21
- filename: fileName,
22
- knownLength: blob.length,
23
- contentType: type,
24
- });
32
+ protected _parts: IPart[] = [];
33
+
34
+ get boundary(): string {
35
+ if (!this._boundary) {
36
+ this._boundary = this._generateBoundary();
25
37
  }
26
- } else if (type) {
27
- blob = PayloadSerializer.deserializeBlobBuffer(value);
28
- if (blob) {
29
- target.append(name, blob, 'blob');
38
+ return this._boundary;
39
+ }
40
+
41
+ protected _generateBoundary(): string {
42
+ let boundary = '--------------------------';
43
+ for (let i = 0; i < 24; i++) {
44
+ boundary += Math.floor(Math.random() * 10).toString(16);
30
45
  }
31
- } else {
32
- target.append(name, value);
46
+ return boundary;
33
47
  }
34
- }
35
48
 
36
- export interface FormDataResult {
37
49
  /**
38
- * The contents of the form data
50
+ *
51
+ * @param parts The serialized FormData body
52
+ * @returns
39
53
  */
40
- buffer: Buffer;
54
+ getBody(parts: IMultipartBody[]): FormDataResult {
55
+ parts.forEach(part => this._append(part));
56
+ const buffer = this._generate();
57
+ return {
58
+ buffer,
59
+ type: this.getContentType(),
60
+ }
61
+ }
62
+
41
63
  /**
42
- * Content type for the form data.
64
+ * @returns The value of the content type header with the generate boundary.
43
65
  */
44
- type: string;
45
- }
46
-
66
+ getContentType(): string {
67
+ return `multipart/form-data; boundary=${this.boundary}`;
68
+ }
47
69
 
48
- function _getData(): Promise<FormDataResult> {
49
- return new Promise((resolve, reject) => {
50
- let result: Buffer;
51
- target.on('data', (data) => {
52
- if (!(data instanceof Buffer)) {
53
- data = Buffer.from(data);
54
- }
55
- if (!result) {
56
- result = data;
57
- } else {
58
- const sum = result.length + data.length;
59
- result = Buffer.concat([result, data], sum);
60
- }
61
- });
62
- target.on('error', (err) => reject(err));
63
- target.on('end', () => {
64
- const ct = target.getHeaders()['content-type'];
65
- resolve({
66
- buffer: result,
67
- type: ct,
70
+ protected _append(part: IMultipartBody): void {
71
+ if (part.enabled === false) {
72
+ return;
73
+ }
74
+ if (typeof part.isFile === 'boolean') {
75
+ this._appendLegacy(part);
76
+ return;
77
+ }
78
+ const { name, value } = part;
79
+ if (typeof value === 'string') {
80
+ this._parts.push({
81
+ name,
82
+ type: 'string',
83
+ contents: Buffer.from(value),
68
84
  });
85
+ return;
86
+ }
87
+ let buffer: Buffer
88
+ if (value.type === 'file') {
89
+ buffer = PayloadSerializer.deserializeFileBuffer(value);
90
+ } else {
91
+ buffer = PayloadSerializer.deserializeBlobBuffer(value);
92
+ }
93
+ this._parts.push({
94
+ name,
95
+ type: 'file',
96
+ contents: buffer,
97
+ meta: value.meta,
69
98
  });
70
- target.resume();
71
- });
99
+ }
100
+
101
+ protected _appendLegacy(part: IMultipartBody): void {
102
+ if (part.isFile) {
103
+ const buffer = PayloadSerializer.deserializeBlobBufferLegacy(part.value as string);
104
+ this._parts.push({
105
+ name: part.name,
106
+ type: 'file',
107
+ contents: buffer,
108
+ meta: {
109
+ mime: this._readDataUrlMime(part.value as string) || DefaultContentType,
110
+ name: part.fileName,
111
+ },
112
+ });
113
+ } else if (part.type) {
114
+ const buffer = PayloadSerializer.deserializeBlobBufferLegacy(part.value as string);
115
+ this._parts.push({
116
+ name: part.name,
117
+ type: 'file',
118
+ contents: buffer,
119
+ meta: {
120
+ mime: part.type || DefaultContentType,
121
+ },
122
+ });
123
+ } else {
124
+ const buffer = Buffer.from(part.value as string);
125
+ this._parts.push({
126
+ name: part.name,
127
+ type: 'string',
128
+ contents: buffer,
129
+ });
130
+ }
131
+ }
132
+
133
+ protected _readDataUrlMime(dataUrl: string): string | undefined {
134
+ const arr = dataUrl.split(',');
135
+ const matchedMime = arr[0].match(/:(.*?);/);
136
+ if (!matchedMime) {
137
+ return undefined;
138
+ }
139
+ return matchedMime[1];
140
+ }
141
+
142
+ protected _generate(): Buffer {
143
+ const result: Buffer[] = [];
144
+ for (const part of this._parts) {
145
+ result.push(this._getHeader(part));
146
+ result.push(part.contents);
147
+ result.push(Buffer.from(LineBreak));
148
+ }
149
+ result.push(this._getFooter());
150
+ return Buffer.concat(result);
151
+ }
152
+
153
+ protected _getHeader(part: IPart): Buffer {
154
+ const disposition: string[] = [
155
+ 'Content-Disposition: form-data',
156
+ `name="${part.name}"`,
157
+ ];
158
+ let mime: string | undefined;
159
+ if (part.type === 'file') {
160
+ const meta = part.meta as IFileMeta;
161
+ if (meta) {
162
+ // "blob" is added by browsers by default.
163
+ disposition.push(`filename="${meta.name || 'blob'}"`);
164
+ mime = meta.mime || DefaultContentType;
165
+ }
166
+ }
167
+
168
+ const headers: string[] = [
169
+ `--${this.boundary}`,
170
+ disposition.join('; '),
171
+ ];
172
+ if (mime) {
173
+ headers.push(`Content-Type: ${mime}`);
174
+ }
175
+ headers.push(LineBreak);
176
+ return Buffer.from(headers.join(LineBreak));
177
+ }
178
+
179
+ protected _getFooter(): Buffer {
180
+ return Buffer.from(`--${this.boundary}--`);
181
+ }
72
182
  }
73
183
 
184
+ // --7MA4YWxkTrZu0gW
185
+ // Content-Disposition: form-data; name="a"; filename="kubectrl-apimodeling.sh"
186
+ // Content-Type: application/x-shellscript
187
+
188
+ // {…file content…}
189
+ // --7MA4YWxkTrZu0gW
190
+ // Content-Disposition: form-data; name="t1"
191
+
192
+ // v1
193
+ // --7MA4YWxkTrZu0gW
194
+ // Content-Disposition: form-data; name="t2"; filename="blob"
195
+ // Content-Type: plain/text
196
+
197
+ // {…file content…}
198
+ // --7MA4YWxkTrZu0gW--
199
+
74
200
  /**
75
201
  * Processes the form data.
76
202
  */
77
- export default async function (parts: IMultipartBody[]): Promise<FormDataResult> {
78
- target = new _FormData();
79
- const promises: Promise<void>[] = [];
80
- parts.forEach((part) => {
81
- promises.push(_append(part));
82
- });
83
- await Promise.all(promises);
84
- return _getData();
203
+ export default function (parts: IMultipartBody[]): FormDataResult {
204
+ const factory = new FormDataNode();
205
+ return factory.getBody(parts);
85
206
  }
@@ -1,6 +1,6 @@
1
1
  import formDataConverter from './FormData.js';
2
2
  import { Headers } from '../../lib/headers/Headers.js';
3
- import { Payload, PayloadSerializer, IMultipartBody } from '../../lib/transformers/PayloadSerializer.js';
3
+ import { Payload, PayloadSerializer, IMultipartBody, IFileMeta, IBlobMeta } from '../../lib/transformers/PayloadSerializer.js';
4
4
 
5
5
  /**
6
6
  * A class containing static helper methods to deal with Payload
@@ -8,9 +8,8 @@ import { Payload, PayloadSerializer, IMultipartBody } from '../../lib/transforme
8
8
  */
9
9
  export class PayloadSupport {
10
10
  /**
11
- * NormalizeLineEndingsToCRLF
12
- * https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/
13
- * platform/text/LineEnding.cpp&rcl=1458041387&l=101
11
+ * Normalizes line endings to CRLF
12
+ * https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/wtf/text/line_ending.cc;l=68
14
13
  *
15
14
  * @param string A string to be normalized.
16
15
  * @return normalized string
@@ -41,28 +40,37 @@ export class PayloadSupport {
41
40
  /**
42
41
  * Transforms the request payload into a `Buffer`
43
42
  *
44
- * @param payload A payload message
45
43
  * @param headers A headers object where to append headers when needed
44
+ * @param payload A payload message
46
45
  * @returns A promise resolved to a `Buffer`.
47
46
  */
48
- static async payloadToBuffer(payload: Payload, headers: Headers): Promise<Buffer|undefined> {
47
+ static payloadToBuffer(headers: Headers, payload?: Payload): Buffer | undefined {
49
48
  if (!payload) {
50
- return;
49
+ return undefined;
51
50
  }
52
51
  if (typeof payload === 'string') {
53
52
  payload = PayloadSupport.normalizeString(payload);
54
53
  return Buffer.from(payload, 'utf8');
55
54
  }
56
-
55
+
57
56
  if (payload.type === 'string') {
58
- return PayloadSupport.payloadToBuffer(payload.data as string, headers);
57
+ return PayloadSupport.payloadToBuffer(headers, payload.data as string);
59
58
  }
60
59
 
61
- if (['blob', 'file'].includes(payload.type)) {
62
- if (!headers.has('content-type') && payload.mime) {
63
- headers.set('content-type', payload.mime);
60
+ if (payload.type === 'file') {
61
+ const meta = payload.meta as IFileMeta;
62
+ if (!headers.has('content-type') && meta.mime) {
63
+ headers.set('content-type', meta.mime);
64
64
  }
65
- return PayloadSerializer.deserializeBlobBuffer(payload.data as string);
65
+ return PayloadSerializer.deserializeFileBuffer(payload);
66
+ }
67
+
68
+ if (payload.type === 'blob') {
69
+ const meta = payload.meta as IBlobMeta;
70
+ if (!headers.has('content-type') && meta.mime) {
71
+ headers.set('content-type', meta.mime);
72
+ }
73
+ return PayloadSerializer.deserializeBlobBuffer(payload);
66
74
  }
67
75
 
68
76
  if (payload.type === 'buffer') {
@@ -74,11 +82,11 @@ export class PayloadSupport {
74
82
  }
75
83
 
76
84
  if (payload.type === 'formdata') {
77
- const result = await formDataConverter(payload.data as IMultipartBody[]);
85
+ const result = formDataConverter(payload.data as IMultipartBody[]);
78
86
  headers.set('Content-Type', result.type);
79
87
  return result.buffer;
80
88
  }
81
-
89
+
82
90
  throw new Error('Unsupported payload message');
83
91
  }
84
92
  }
@@ -1,7 +0,0 @@
1
- /**
2
- * Converts blob data to base64 string.
3
- *
4
- * @param blob File or blob object to be translated to string
5
- * @return Promise resolved to a base64 string data from the file.
6
- */
7
- export declare function blobToDataUrl(blob: Blob): Promise<string>;
@@ -1,19 +0,0 @@
1
- /**
2
- * Converts blob data to base64 string.
3
- *
4
- * @param blob File or blob object to be translated to string
5
- * @return Promise resolved to a base64 string data from the file.
6
- */
7
- export function blobToDataUrl(blob) {
8
- return new Promise((resolve, reject) => {
9
- const reader = new FileReader();
10
- reader.onloadend = () => {
11
- resolve(String(reader.result));
12
- };
13
- reader.onerror = () => {
14
- reject(new Error('Unable to convert blob to string.'));
15
- };
16
- reader.readAsDataURL(blob);
17
- });
18
- }
19
- //# sourceMappingURL=Utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../../../src/lib/transformers/Utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,CAAC,SAAS,GAAG,GAAS,EAAE;YAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC;QACF,MAAM,CAAC,OAAO,GAAG,GAAS,EAAE;YAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC;QACF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,18 +0,0 @@
1
- /**
2
- * Converts blob data to base64 string.
3
- *
4
- * @param blob File or blob object to be translated to string
5
- * @return Promise resolved to a base64 string data from the file.
6
- */
7
- export function blobToDataUrl(blob: Blob): Promise<string> {
8
- return new Promise((resolve, reject) => {
9
- const reader = new FileReader();
10
- reader.onloadend = (): void => {
11
- resolve(String(reader.result));
12
- };
13
- reader.onerror = (): void => {
14
- reject(new Error('Unable to convert blob to string.'));
15
- };
16
- reader.readAsDataURL(blob);
17
- });
18
- }