@emeryld/rrroutes-client 2.5.6 → 2.5.7

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.cjs CHANGED
@@ -160,10 +160,69 @@ function buildUrl(leaf, baseUrl, params, query) {
160
160
  const url = `${baseUrl ?? ""}${path}${toSearchString(normalizedQuery)}`;
161
161
  return { url, normalizedQuery, normalizedParams };
162
162
  }
163
- function toFormData(body) {
163
+ function isBlobLike(value) {
164
+ return typeof Blob !== "undefined" && value instanceof Blob;
165
+ }
166
+ function assertFileFieldValue(sourceKey, targetField, value) {
167
+ if (value == null) return;
168
+ if (isBlobLike(value)) return;
169
+ if (typeof FileList !== "undefined" && value instanceof FileList) {
170
+ for (const item of Array.from(value)) {
171
+ if (!isBlobLike(item)) {
172
+ throw new Error(
173
+ `Multipart field "${sourceKey}" must contain Blob/File values for "${targetField}".`
174
+ );
175
+ }
176
+ }
177
+ return;
178
+ }
179
+ if (Array.isArray(value)) {
180
+ for (const item of value) {
181
+ if (!isBlobLike(item)) {
182
+ throw new Error(
183
+ `Multipart field "${sourceKey}" must contain Blob/File values for "${targetField}".`
184
+ );
185
+ }
186
+ }
187
+ return;
188
+ }
189
+ throw new Error(
190
+ `Multipart field "${sourceKey}" must be Blob/File, Blob[] or FileList.`
191
+ );
192
+ }
193
+ function splitMultipartBody(body, fields) {
194
+ if (!body || typeof body !== "object" || Array.isArray(body) || !fields) {
195
+ return {
196
+ regularBody: body,
197
+ multipartFiles: {}
198
+ };
199
+ }
200
+ const fieldByInputKey = /* @__PURE__ */ new Map();
201
+ for (const field of fields) {
202
+ fieldByInputKey.set(`file${field.name}`, field.name);
203
+ fieldByInputKey.set(field.name, field.name);
204
+ }
205
+ const regularBody = {};
206
+ const multipartFiles = {};
207
+ for (const [key, value] of Object.entries(body)) {
208
+ const resolvedFieldName = fieldByInputKey.get(key);
209
+ if (!resolvedFieldName) {
210
+ regularBody[key] = value;
211
+ continue;
212
+ }
213
+ assertFileFieldValue(key, resolvedFieldName, value);
214
+ multipartFiles[resolvedFieldName] = value;
215
+ }
216
+ return { regularBody, multipartFiles };
217
+ }
218
+ function toFormData(body, fields) {
219
+ const fileFieldNames = new Set((fields ?? []).map((field) => field.name));
164
220
  const fd = new FormData();
165
221
  for (const [k, v] of Object.entries(body ?? {})) {
166
222
  if (v == null) continue;
223
+ if (fileFieldNames.has(k)) {
224
+ assertFileFieldValue(`file${k}`, k, v);
225
+ }
167
226
  if (Array.isArray(v)) {
168
227
  for (const item of v) {
169
228
  if (item == null) continue;
@@ -308,9 +367,19 @@ function buildGetLeaf(leaf, rqOpts, env) {
308
367
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
309
368
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
310
369
  if (typeof options?.body !== "undefined") {
311
- const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract2.lowProfileParse)(leafCfg.bodySchema, options.body) : options.body;
370
+ const { regularBody, multipartFiles } = splitMultipartBody(
371
+ options.body,
372
+ leafCfg.bodyFiles
373
+ );
374
+ const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract2.lowProfileParse)(leafCfg.bodySchema, regularBody) : regularBody;
312
375
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
313
- payload = toFormData(normalizedBody);
376
+ payload = toFormData(
377
+ {
378
+ ...normalizedBody,
379
+ ...multipartFiles
380
+ },
381
+ leafCfg.bodyFiles
382
+ );
314
383
  } else {
315
384
  payload = normalizedBody;
316
385
  }
@@ -571,9 +640,19 @@ function buildInfiniteGetLeaf(leaf, rqOpts, env) {
571
640
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
572
641
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
573
642
  if (typeof options?.body !== "undefined") {
574
- const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract3.lowProfileParse)(leafCfg.bodySchema, options.body) : options.body;
643
+ const { regularBody, multipartFiles } = splitMultipartBody(
644
+ options.body,
645
+ leafCfg.bodyFiles
646
+ );
647
+ const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract3.lowProfileParse)(leafCfg.bodySchema, regularBody) : regularBody;
575
648
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
576
- payload = toFormData(normalizedBody);
649
+ payload = toFormData(
650
+ {
651
+ ...normalizedBody,
652
+ ...multipartFiles
653
+ },
654
+ leafCfg.bodyFiles
655
+ );
577
656
  } else {
578
657
  payload = normalizedBody;
579
658
  }
@@ -869,9 +948,19 @@ function buildMutationLeaf(leaf, rqOpts, env) {
869
948
  const acceptsBody = Boolean(leafCfg.bodySchema || isMultipart);
870
949
  const requiresBody = options?.requireBody ?? (!acceptsBody ? false : true);
871
950
  if (typeof options?.body !== "undefined") {
872
- const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract4.lowProfileParse)(leafCfg.bodySchema, options.body) : options.body;
951
+ const { regularBody, multipartFiles } = splitMultipartBody(
952
+ options.body,
953
+ leafCfg.bodyFiles
954
+ );
955
+ const normalizedBody = leafCfg.bodySchema ? (0, import_rrroutes_contract4.lowProfileParse)(leafCfg.bodySchema, regularBody) : regularBody;
873
956
  if (isMultipart && normalizedBody && typeof normalizedBody === "object") {
874
- payload = toFormData(normalizedBody);
957
+ payload = toFormData(
958
+ {
959
+ ...normalizedBody,
960
+ ...multipartFiles
961
+ },
962
+ leafCfg.bodyFiles
963
+ );
875
964
  } else {
876
965
  payload = normalizedBody;
877
966
  }