import { version } from "./dist-package-DpScpvTA.js";
import { createUTReporter, createDeferred, generateTraceHeaders } from "./dist-ut-reporter-Dlppchbx.js";
import { logDeprecationWarning } from "./dist-deprecations-pLmw6Ytd.js";
import { ensure } from "../effect/dist-esm-Array.js";
import { runPromise, provideService, runPromiseExit, causeSquash, map, tap, flatMap, async, forEach, succeed, sync } from "../effect/dist-esm-Micro.js";
import { createIdentityProxy, resolveMaybeUrlArg, FetchContext, fetchEff, UploadPausedError, UploadAbortedError, UploadThingError } from "../@uploadthing/shared/dist-index.js";
import { unsafeCoerce } from "../effect/dist-esm-Function.js";
import { isRecord, hasProperty } from "../effect/dist-esm-Predicate.js";
const uploadWithProgress = (file, rangeStart, presigned, opts) => async((resume) => {
  const xhr = new XMLHttpRequest();
  xhr.open("PUT", presigned.url, true);
  xhr.setRequestHeader("Range", `bytes=${rangeStart}-`);
  xhr.setRequestHeader("x-uploadthing-version", version);
  xhr.setRequestHeader("b3", opts.traceHeaders.b3);
  xhr.setRequestHeader("traceparent", opts.traceHeaders.traceparent);
  xhr.responseType = "json";
  let previousLoaded = 0;
  xhr.upload.addEventListener("progress", ({ loaded }) => {
    const delta = loaded - previousLoaded;
    opts.onUploadProgress?.({
      loaded,
      delta
    });
    previousLoaded = loaded;
  });
  xhr.addEventListener("load", () => {
    if (xhr.status >= 200 && xhr.status < 300 && isRecord(xhr.response)) if (hasProperty(xhr.response, "error")) resume(new UploadThingError({
      code: "UPLOAD_FAILED",
      message: String(xhr.response.error),
      data: xhr.response
    }));
    else resume(succeed(xhr.response));
    else resume(new UploadThingError({
      code: "UPLOAD_FAILED",
      message: `XHR failed ${xhr.status} ${xhr.statusText}`,
      data: xhr.response
    }));
  });
  xhr.addEventListener("error", () => {
    resume(new UploadThingError({ code: "UPLOAD_FAILED" }));
  });
  const formData = new FormData();
  if ("uri" in file) formData.append("file", {
    uri: file.uri,
    type: file.type,
    name: file.name,
    ...rangeStart > 0 && { range: rangeStart }
  });
  else formData.append("file", rangeStart > 0 ? file.slice(rangeStart) : file);
  xhr.send(formData);
  return sync(() => xhr.abort());
});
const uploadFile = (file, presigned, opts) => fetchEff(presigned.url, {
  method: "HEAD",
  headers: opts.traceHeaders
}).pipe(map(({ headers }) => parseInt(headers.get("x-ut-range-start") ?? "0", 10)), tap((start) => opts.onUploadProgress?.({
  delta: start,
  loaded: start
})), flatMap((start) => uploadWithProgress(file, start, presigned, {
  traceHeaders: opts.traceHeaders,
  onUploadProgress: (progressEvent) => opts.onUploadProgress?.({
    delta: progressEvent.delta,
    loaded: progressEvent.loaded + start
  })
})), map(unsafeCoerce), map((uploadResponse) => ({
  name: file.name,
  size: file.size,
  key: presigned.key,
  lastModified: file.lastModified,
  serverData: uploadResponse.serverData,
  get url() {
    logDeprecationWarning("`file.url` is deprecated and will be removed in uploadthing v9. Use `file.ufsUrl` instead.");
    return uploadResponse.url;
  },
  get appUrl() {
    logDeprecationWarning("`file.appUrl` is deprecated and will be removed in uploadthing v9. Use `file.ufsUrl` instead.");
    return uploadResponse.appUrl;
  },
  ufsUrl: uploadResponse.ufsUrl,
  customId: presigned.customId,
  type: file.type,
  fileHash: uploadResponse.fileHash
})));
const uploadFilesInternal = (endpoint, opts) => {
  const traceHeaders = generateTraceHeaders();
  const reportEventToUT = createUTReporter({
    endpoint: String(endpoint),
    package: opts.package,
    url: opts.url,
    headers: opts.headers,
    traceHeaders
  });
  const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0);
  let totalLoaded = 0;
  return flatMap(reportEventToUT("upload", {
    input: "input" in opts ? opts.input : null,
    files: opts.files.map((f) => ({
      name: f.name,
      size: f.size,
      type: f.type,
      lastModified: f.lastModified
    }))
  }), (presigneds) => forEach(presigneds, (presigned, i) => flatMap(sync(() => opts.onUploadBegin?.({ file: opts.files[i].name })), () => uploadFile(opts.files[i], presigned, {
    traceHeaders,
    onUploadProgress: (ev) => {
      totalLoaded += ev.delta;
      opts.onUploadProgress?.({
        file: opts.files[i],
        progress: ev.loaded / opts.files[i].size * 100,
        loaded: ev.loaded,
        delta: ev.delta,
        totalLoaded,
        totalProgress: totalLoaded / totalSize
      });
    }
  })), { concurrency: 6 }));
};
const version$1 = version;
const genUploader = (initOpts) => {
  const routeRegistry = createIdentityProxy();
  const controllableUpload = async (slug, opts) => {
    const uploads = /* @__PURE__ */ new Map();
    const endpoint = typeof slug === "function" ? slug(routeRegistry) : slug;
    const traceHeaders = generateTraceHeaders();
    const utReporter = createUTReporter({
      endpoint: String(endpoint),
      package: initOpts?.package ?? "uploadthing/client",
      url: resolveMaybeUrlArg(initOpts?.url),
      headers: opts.headers,
      traceHeaders
    });
    const fetchFn = initOpts?.fetch ?? window.fetch;
    const presigneds = await runPromise(utReporter("upload", {
      input: "input" in opts ? opts.input : null,
      files: opts.files.map((f) => ({
        name: f.name,
        size: f.size,
        type: f.type,
        lastModified: f.lastModified
      }))
    }).pipe(provideService(FetchContext, fetchFn)));
    const totalSize = opts.files.reduce((acc, f) => acc + f.size, 0);
    let totalLoaded = 0;
    const uploadEffect = (file, presigned) => uploadFile(file, presigned, {
      traceHeaders,
      onUploadProgress: (progressEvent) => {
        totalLoaded += progressEvent.delta;
        opts.onUploadProgress?.({
          ...progressEvent,
          file,
          progress: Math.round(progressEvent.loaded / file.size * 100),
          totalLoaded,
          totalProgress: Math.round(totalLoaded / totalSize * 100)
        });
      }
    }).pipe(provideService(FetchContext, fetchFn));
    for (const [i, p] of presigneds.entries()) {
      const file = opts.files[i];
      if (!file) continue;
      const deferred = createDeferred();
      uploads.set(file, {
        deferred,
        presigned: p
      });
      runPromiseExit(uploadEffect(file, p), { signal: deferred.ac.signal }).then((result) => {
        if (result._tag === "Success") return deferred.resolve(result.value);
        else if (result.cause._tag === "Interrupt") throw new UploadPausedError();
        throw causeSquash(result.cause);
      }).catch((err) => {
        if (err instanceof UploadPausedError) return;
        deferred.reject(err);
      });
    }
    const pauseUpload = (file) => {
      const files = ensure(file ?? opts.files);
      for (const file$1 of files) {
        const upload = uploads.get(file$1);
        if (!upload) return;
        if (upload.deferred.ac.signal.aborted) throw new UploadAbortedError();
        upload.deferred.ac.abort();
      }
    };
    const resumeUpload = (file) => {
      const files = ensure(file ?? opts.files);
      for (const file$1 of files) {
        const upload = uploads.get(file$1);
        if (!upload) throw "No upload found";
        upload.deferred.ac = new AbortController();
        runPromiseExit(uploadEffect(file$1, upload.presigned), { signal: upload.deferred.ac.signal }).then((result) => {
          if (result._tag === "Success") return upload.deferred.resolve(result.value);
          else if (result.cause._tag === "Interrupt") throw new UploadPausedError();
          throw causeSquash(result.cause);
        }).catch((err) => {
          if (err instanceof UploadPausedError) return;
          upload.deferred.reject(err);
        });
      }
    };
    const done = async (file) => {
      const promises = [];
      const files = ensure(file ?? opts.files);
      for (const file$1 of files) {
        const upload = uploads.get(file$1);
        if (!upload) throw "No upload found";
        promises.push(upload.deferred.promise);
      }
      const results = await Promise.all(promises);
      return file ? results[0] : results;
    };
    return {
      pauseUpload,
      resumeUpload,
      done
    };
  };
  const typedUploadFiles = (slug, opts) => {
    const endpoint = typeof slug === "function" ? slug(routeRegistry) : slug;
    const fetchFn = initOpts?.fetch ?? window.fetch;
    return uploadFilesInternal(endpoint, {
      ...opts,
      skipPolling: {},
      url: resolveMaybeUrlArg(initOpts?.url),
      package: initOpts?.package ?? "uploadthing/client",
      input: opts.input
    }).pipe(provideService(FetchContext, fetchFn), (effect) => runPromiseExit(effect, opts.signal && { signal: opts.signal })).then((exit) => {
      if (exit._tag === "Success") return exit.value;
      else if (exit.cause._tag === "Interrupt") throw new UploadAbortedError();
      throw causeSquash(exit.cause);
    });
  };
  return {
    uploadFiles: typedUploadFiles,
    createUpload: controllableUpload,
    routeRegistry
  };
};
export {
  UploadAbortedError,
  UploadPausedError,
  genUploader,
  version$1 as version
};
//# sourceMappingURL=client-index.js.map
