@emeryld/rrroutes-client 2.5.6 → 2.5.8

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/index.mjs CHANGED
@@ -119,25 +119,101 @@ function buildUrl(leaf, baseUrl, params, query) {
119
119
  const url = `${baseUrl ?? ""}${path}${toSearchString(normalizedQuery)}`;
120
120
  return { url, normalizedQuery, normalizedParams };
121
121
  }
122
- function toFormData(body) {
123
- const fd = new FormData();
124
- for (const [k, v] of Object.entries(body ?? {})) {
125
- if (v == null) continue;
126
- if (Array.isArray(v)) {
127
- for (const item of v) {
128
- if (item == null) continue;
129
- fd.append(k, item);
122
+ function isBlobLike(value) {
123
+ return typeof Blob !== "undefined" && value instanceof Blob;
124
+ }
125
+ function assertFileFieldValue(sourceKey, targetField, value) {
126
+ if (value == null) return;
127
+ if (isBlobLike(value)) return;
128
+ if (typeof FileList !== "undefined" && value instanceof FileList) {
129
+ for (const item of Array.from(value)) {
130
+ if (!isBlobLike(item)) {
131
+ throw new Error(
132
+ `Multipart field "${sourceKey}" must contain Blob/File values for "${targetField}".`
133
+ );
130
134
  }
135
+ }
136
+ return;
137
+ }
138
+ if (Array.isArray(value)) {
139
+ for (const item of value) {
140
+ if (!isBlobLike(item)) {
141
+ throw new Error(
142
+ `Multipart field "${sourceKey}" must contain Blob/File values for "${targetField}".`
143
+ );
144
+ }
145
+ }
146
+ return;
147
+ }
148
+ throw new Error(
149
+ `Multipart field "${sourceKey}" must be Blob/File, Blob[] or FileList.`
150
+ );
151
+ }
152
+ function splitMultipartBody(body, fields) {
153
+ if (!body || typeof body !== "object" || Array.isArray(body) || !fields) {
154
+ return {
155
+ regularBody: body,
156
+ multipartFiles: {}
157
+ };
158
+ }
159
+ const fieldByInputKey = /* @__PURE__ */ new Map();
160
+ for (const field of fields) {
161
+ fieldByInputKey.set(`file_${field.name}`, field.name);
162
+ fieldByInputKey.set(field.name, field.name);
163
+ }
164
+ const regularBody = {};
165
+ const multipartFiles = {};
166
+ for (const [key, value] of Object.entries(body)) {
167
+ const resolvedFieldName = fieldByInputKey.get(key);
168
+ if (!resolvedFieldName) {
169
+ regularBody[key] = value;
131
170
  continue;
132
171
  }
133
- if (typeof FileList !== "undefined" && v instanceof FileList) {
134
- for (const item of Array.from(v)) {
135
- if (item == null) continue;
136
- fd.append(k, item);
172
+ assertFileFieldValue(key, resolvedFieldName, value);
173
+ multipartFiles[resolvedFieldName] = value;
174
+ }
175
+ return { regularBody, multipartFiles };
176
+ }
177
+ var isReactNativeFile = (v) => v && typeof v === "object" && typeof v.uri === "string" && typeof v.name === "string" && typeof v.type === "string";
178
+ var isFile = (v) => typeof File !== "undefined" && v instanceof File;
179
+ var isBlob = (v) => typeof Blob !== "undefined" && v instanceof Blob;
180
+ function toFormData(body, bodyFiles = []) {
181
+ const fd = new FormData();
182
+ const fileKeys = new Set(
183
+ bodyFiles.flatMap((f) => [f.name, `file_${f.name}`])
184
+ );
185
+ const appendFile = (fieldName, value) => {
186
+ if (value == null) return;
187
+ if (isReactNativeFile(value)) {
188
+ fd.append(fieldName, value);
189
+ return;
190
+ }
191
+ if (isFile(value)) {
192
+ fd.append(fieldName, value, value.name);
193
+ return;
194
+ }
195
+ if (isBlob(value)) {
196
+ fd.append(fieldName, value, "upload");
197
+ return;
198
+ }
199
+ throw new Error(`Invalid upload value for ${fieldName}`);
200
+ };
201
+ for (const [key, value] of Object.entries(body)) {
202
+ if (fileKeys.has(key)) {
203
+ if (Array.isArray(value)) {
204
+ for (const v of value) appendFile(key, v);
205
+ } else {
206
+ appendFile(key, value);
137
207
  }
138
208
  continue;
139
209
  }
140
- fd.append(k, v);
210
+ if (typeof value === "string") fd.append(key, value);
211
+ else if (typeof value === "number" || typeof value === "boolean")
212
+ fd.append(key, String(value));
213
+ else if (value == null) {
214
+ } else {
215
+ fd.append(key, JSON.stringify(value));
216
+ }
141
217
  }
142
218
  return fd;
143
219
  }
@@ -267,9 +343,19 @@ function buildGetLeaf(leaf, rqOpts, env) {
267
343
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
268
344
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
269
345
  if (typeof options?.body !== "undefined") {
270
- const normalizedBody = leafCfg.bodySchema ? lowProfileParse2(leafCfg.bodySchema, options.body) : options.body;
346
+ const { regularBody, multipartFiles } = splitMultipartBody(
347
+ options.body,
348
+ leafCfg.bodyFiles
349
+ );
350
+ const normalizedBody = leafCfg.bodySchema ? lowProfileParse2(leafCfg.bodySchema, regularBody) : regularBody;
271
351
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
272
- payload = toFormData(normalizedBody);
352
+ payload = toFormData(
353
+ {
354
+ ...normalizedBody,
355
+ ...multipartFiles
356
+ },
357
+ leafCfg.bodyFiles
358
+ );
273
359
  } else {
274
360
  payload = normalizedBody;
275
361
  }
@@ -533,9 +619,19 @@ function buildInfiniteGetLeaf(leaf, rqOpts, env) {
533
619
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
534
620
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
535
621
  if (typeof options?.body !== "undefined") {
536
- const normalizedBody = leafCfg.bodySchema ? lowProfileParse3(leafCfg.bodySchema, options.body) : options.body;
622
+ const { regularBody, multipartFiles } = splitMultipartBody(
623
+ options.body,
624
+ leafCfg.bodyFiles
625
+ );
626
+ const normalizedBody = leafCfg.bodySchema ? lowProfileParse3(leafCfg.bodySchema, regularBody) : regularBody;
537
627
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
538
- payload = toFormData(normalizedBody);
628
+ payload = toFormData(
629
+ {
630
+ ...normalizedBody,
631
+ ...multipartFiles
632
+ },
633
+ leafCfg.bodyFiles
634
+ );
539
635
  } else {
540
636
  payload = normalizedBody;
541
637
  }
@@ -833,9 +929,19 @@ function buildMutationLeaf(leaf, rqOpts, env) {
833
929
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
834
930
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
835
931
  if (typeof options?.body !== "undefined") {
836
- const normalizedBody = leafCfg.bodySchema ? lowProfileParse4(leafCfg.bodySchema, options.body) : options.body;
932
+ const { regularBody, multipartFiles } = splitMultipartBody(
933
+ options.body,
934
+ leafCfg.bodyFiles
935
+ );
936
+ const normalizedBody = leafCfg.bodySchema ? lowProfileParse4(leafCfg.bodySchema, regularBody) : regularBody;
837
937
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
838
- payload = toFormData(normalizedBody);
938
+ payload = toFormData(
939
+ {
940
+ ...normalizedBody,
941
+ ...multipartFiles
942
+ },
943
+ leafCfg.bodyFiles
944
+ );
839
945
  } else {
840
946
  payload = normalizedBody;
841
947
  }