@emeryld/rrroutes-client 2.5.7 → 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/README.md CHANGED
@@ -222,7 +222,9 @@ const updateUser = buildRoute('updateUser', {}, { name: 'profile' }) // debug na
222
222
 
223
223
  ### 8) File uploads (FormData)
224
224
 
225
- If a leaf has `bodyFiles` set in its contract, the client automatically converts the body to `FormData`:
225
+ If a leaf has `bodyFiles` set in its contract, the client automatically converts the body to `FormData`.
226
+ For each declared file field name, pass files using `file${name}` in the input body.
227
+ The raw field name is also accepted for backward compatibility.
226
228
 
227
229
  ```ts
228
230
  const uploadAvatar = client.build(
@@ -231,7 +233,21 @@ const uploadAvatar = client.build(
231
233
 
232
234
  await uploadAvatar.fetch(
233
235
  { params: { userId: 'u_1' } },
234
- { avatar: new File([blob], 'avatar.png', { type: 'image/png' }) },
236
+ {
237
+ // bodyFiles: [{ name: 'avatar', maxCount: 1 }]
238
+ fileavatar: new File([blob], 'avatar.png', { type: 'image/png' }),
239
+ // any non-file body fields still go here and are validated by bodySchema
240
+ note: 'profile image',
241
+ },
242
+ )
243
+ ```
244
+
245
+ For multi-file fields (`maxCount > 1`), pass `Blob[]` or `FileList`:
246
+
247
+ ```ts
248
+ await uploadAvatar.fetch(
249
+ { params: { userId: 'u_1' } },
250
+ { filephotos: [fileA, fileB] }, // bodyFiles: [{ name: 'photos', maxCount: 5 }]
235
251
  )
236
252
  ```
237
253
 
package/dist/index.cjs CHANGED
@@ -199,7 +199,7 @@ function splitMultipartBody(body, fields) {
199
199
  }
200
200
  const fieldByInputKey = /* @__PURE__ */ new Map();
201
201
  for (const field of fields) {
202
- fieldByInputKey.set(`file${field.name}`, field.name);
202
+ fieldByInputKey.set(`file_${field.name}`, field.name);
203
203
  fieldByInputKey.set(field.name, field.name);
204
204
  }
205
205
  const regularBody = {};
@@ -215,29 +215,46 @@ function splitMultipartBody(body, fields) {
215
215
  }
216
216
  return { regularBody, multipartFiles };
217
217
  }
218
- function toFormData(body, fields) {
219
- const fileFieldNames = new Set((fields ?? []).map((field) => field.name));
218
+ var isReactNativeFile = (v) => v && typeof v === "object" && typeof v.uri === "string" && typeof v.name === "string" && typeof v.type === "string";
219
+ var isFile = (v) => typeof File !== "undefined" && v instanceof File;
220
+ var isBlob = (v) => typeof Blob !== "undefined" && v instanceof Blob;
221
+ function toFormData(body, bodyFiles = []) {
220
222
  const fd = new FormData();
221
- for (const [k, v] of Object.entries(body ?? {})) {
222
- if (v == null) continue;
223
- if (fileFieldNames.has(k)) {
224
- assertFileFieldValue(`file${k}`, k, v);
223
+ const fileKeys = new Set(
224
+ bodyFiles.flatMap((f) => [f.name, `file_${f.name}`])
225
+ );
226
+ const appendFile = (fieldName, value) => {
227
+ if (value == null) return;
228
+ if (isReactNativeFile(value)) {
229
+ fd.append(fieldName, value);
230
+ return;
225
231
  }
226
- if (Array.isArray(v)) {
227
- for (const item of v) {
228
- if (item == null) continue;
229
- fd.append(k, item);
230
- }
231
- continue;
232
+ if (isFile(value)) {
233
+ fd.append(fieldName, value, value.name);
234
+ return;
232
235
  }
233
- if (typeof FileList !== "undefined" && v instanceof FileList) {
234
- for (const item of Array.from(v)) {
235
- if (item == null) continue;
236
- fd.append(k, item);
236
+ if (isBlob(value)) {
237
+ fd.append(fieldName, value, "upload");
238
+ return;
239
+ }
240
+ throw new Error(`Invalid upload value for ${fieldName}`);
241
+ };
242
+ for (const [key, value] of Object.entries(body)) {
243
+ if (fileKeys.has(key)) {
244
+ if (Array.isArray(value)) {
245
+ for (const v of value) appendFile(key, v);
246
+ } else {
247
+ appendFile(key, value);
237
248
  }
238
249
  continue;
239
250
  }
240
- fd.append(k, v);
251
+ if (typeof value === "string") fd.append(key, value);
252
+ else if (typeof value === "number" || typeof value === "boolean")
253
+ fd.append(key, String(value));
254
+ else if (value == null) {
255
+ } else {
256
+ fd.append(key, JSON.stringify(value));
257
+ }
241
258
  }
242
259
  return fd;
243
260
  }