"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const packageDpScpvTA = require("./dist-package-DpScpvTA.cjs");
const utReporterDlppchbx = require("./dist-ut-reporter-Dlppchbx.cjs");
const deprecationsPLmw6Ytd = require("./dist-deprecations-pLmw6Ytd.cjs");
const Array = require("../effect/dist-esm-Array.cjs");
const Micro = require("../effect/dist-esm-Micro.cjs");
const index = require("../@uploadthing/shared/dist-index.cjs");
const Function = require("../effect/dist-esm-Function.cjs");
const Predicate = require("../effect/dist-esm-Predicate.cjs");
const uploadWithProgress = (file, rangeStart, presigned, opts) => Micro.async((resume) => {
  const xhr = new XMLHttpRequest();
  xhr.open("PUT", presigned.url, true);
  xhr.setRequestHeader("Range", `bytes=${rangeStart}-`);
  xhr.setRequestHeader("x-uploadthing-version", packageDpScpvTA.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 && Predicate.isRecord(xhr.response)) if (Predicate.hasProperty(xhr.response, "error")) resume(new index.UploadThingError({
      code: "UPLOAD_FAILED",
      message: String(xhr.response.error),
      data: xhr.response
    }));
    else resume(Micro.succeed(xhr.response));
    else resume(new index.UploadThingError({
      code: "UPLOAD_FAILED",
      message: `XHR failed ${xhr.status} ${xhr.statusText}`,
      data: xhr.response
    }));
  });
  xhr.addEventListener("error", () => {
    resume(new index.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 Micro.sync(() => xhr.abort());
});
const uploadFile = (file, presigned, opts) => index.fetchEff(presigned.url, {
  method: "HEAD",
  headers: opts.traceHeaders
}).pipe(Micro.map(({ headers }) => parseInt(headers.get("x-ut-range-start") ?? "0", 10)), Micro.tap((start) => opts.onUploadProgress?.({
  delta: start,
  loaded: start
})), Micro.flatMap((start) => uploadWithProgress(file, start, presigned, {
  traceHeaders: opts.traceHeaders,
  onUploadProgress: (progressEvent) => opts.onUploadProgress?.({
    delta: progressEvent.delta,
    loaded: progressEvent.loaded + start
  })
})), Micro.map(Function.unsafeCoerce), Micro.map((uploadResponse) => ({
  name: file.name,
  size: file.size,
  key: presigned.key,
  lastModified: file.lastModified,
  serverData: uploadResponse.serverData,
  get url() {
    deprecationsPLmw6Ytd.logDeprecationWarning("`file.url` is deprecated and will be removed in uploadthing v9. Use `file.ufsUrl` instead.");
    return uploadResponse.url;
  },
  get appUrl() {
    deprecationsPLmw6Ytd.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 = utReporterDlppchbx.generateTraceHeaders();
  const reportEventToUT = utReporterDlppchbx.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 Micro.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) => Micro.forEach(presigneds, (presigned, i) => Micro.flatMap(Micro.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 = packageDpScpvTA.version;
const genUploader = (initOpts) => {
  const routeRegistry = index.createIdentityProxy();
  const controllableUpload = async (slug, opts) => {
    const uploads = /* @__PURE__ */ new Map();
    const endpoint = typeof slug === "function" ? slug(routeRegistry) : slug;
    const traceHeaders = utReporterDlppchbx.generateTraceHeaders();
    const utReporter = utReporterDlppchbx.createUTReporter({
      endpoint: String(endpoint),
      package: initOpts?.package ?? "uploadthing/client",
      url: index.resolveMaybeUrlArg(initOpts?.url),
      headers: opts.headers,
      traceHeaders
    });
    const fetchFn = initOpts?.fetch ?? window.fetch;
    const presigneds = await Micro.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(Micro.provideService(index.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(Micro.provideService(index.FetchContext, fetchFn));
    for (const [i, p] of presigneds.entries()) {
      const file = opts.files[i];
      if (!file) continue;
      const deferred = utReporterDlppchbx.createDeferred();
      uploads.set(file, {
        deferred,
        presigned: p
      });
      Micro.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 index.UploadPausedError();
        throw Micro.causeSquash(result.cause);
      }).catch((err) => {
        if (err instanceof index.UploadPausedError) return;
        deferred.reject(err);
      });
    }
    const pauseUpload = (file) => {
      const files = Array.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 index.UploadAbortedError();
        upload.deferred.ac.abort();
      }
    };
    const resumeUpload = (file) => {
      const files = Array.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();
        Micro.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 index.UploadPausedError();
          throw Micro.causeSquash(result.cause);
        }).catch((err) => {
          if (err instanceof index.UploadPausedError) return;
          upload.deferred.reject(err);
        });
      }
    };
    const done = async (file) => {
      const promises = [];
      const files = Array.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: index.resolveMaybeUrlArg(initOpts?.url),
      package: initOpts?.package ?? "uploadthing/client",
      input: opts.input
    }).pipe(Micro.provideService(index.FetchContext, fetchFn), (effect) => Micro.runPromiseExit(effect, opts.signal && { signal: opts.signal })).then((exit) => {
      if (exit._tag === "Success") return exit.value;
      else if (exit.cause._tag === "Interrupt") throw new index.UploadAbortedError();
      throw Micro.causeSquash(exit.cause);
    });
  };
  return {
    uploadFiles: typedUploadFiles,
    createUpload: controllableUpload,
    routeRegistry
  };
};
exports.UploadAbortedError = index.UploadAbortedError;
exports.UploadPausedError = index.UploadPausedError;
exports.genUploader = genUploader;
exports.version = version$1;
//# sourceMappingURL=client-index.cjs.map
