{"version":3,"file":"uploader.mjs","names":[],"sources":["../src/uploader/errors.ts","../src/uploader/uploader.util.ts","../src/uploader/completed-upload.model.ts","../src/uploader/uploader.context.ts","../src/uploader/components/uploader-root.tsx","../src/uploader/components/uploader-uploads.tsx","../src/uploader/components/namespace.tsx","../src/uploader/part.model.ts","../src/uploader/file.model.ts","../src/uploader/uploader.model.ts","../src/uploader/use-uploader.ts"],"sourcesContent":["export type UploadErrorType =\n  | \"REJECTED\"\n  | \"REQUEST\"\n  | \"PART\"\n  | \"PART_SIZES\"\n  | \"COMPLETE\"\n  | \"ABORTED\";\n\nexport interface UploadErrorOptions {\n  message?: string;\n  cause?: unknown;\n  /** Name of the file the failure concerns. Feeds the default message. */\n  fileName?: string;\n  /** HTTP status for `PART` failures. `0` means no response at all: network error or stall. */\n  status?: number;\n  /** Parsed `Retry-After`, in ms. Honored by the retry policy when present. */\n  retryAfterMs?: number;\n}\n\nconst defaultMessage = (type: UploadErrorType, fileName?: string): string => {\n  const file = fileName ? `'${fileName}'` : \"the file\";\n  switch (type) {\n    case \"REJECTED\":\n      return `${file} was rejected before it was added.`;\n    case \"REQUEST\":\n      return `Could not start the upload for ${file}.`;\n    case \"PART\":\n      return `A part of ${file} failed to upload.`;\n    case \"PART_SIZES\":\n      return `The requested part sizes for ${file} do not add up to the file's size.`;\n    case \"COMPLETE\":\n      return `The upload of ${file} could not be finalized.`;\n    case \"ABORTED\":\n      return `The upload of ${file} was aborted.`;\n  }\n};\n\n/**\n * The single error type the uploader surfaces. `type` discriminates the failure source; when the\n * uploader wraps an application-level error (a rejected `requestUpload`, a transport failure), the\n * original is preserved on the standard `cause` property.\n *\n * Consumers may also throw `UploadError` from `requestUpload` / `completeUpload` — e.g.\n * `throw new UploadError(\"REQUEST\", { message: \"Daily quota reached.\" })` — and it passes through\n * unwrapped, so the message reaches `file.error` verbatim.\n */\nexport class UploadError extends Error {\n  readonly type: UploadErrorType;\n  readonly fileName?: string;\n  readonly status?: number;\n  readonly retryAfterMs?: number;\n\n  /**\n   * Whether this is a bug in the integration rather than a runtime condition. Dev-bug errors are\n   * logged unconditionally in addition to being surfaced, and are never retried — retrying a\n   * contract violation only hides it.\n   */\n  get dev(): boolean {\n    return this.type === \"PART_SIZES\";\n  }\n\n  constructor(type: UploadErrorType, options?: UploadErrorOptions) {\n    super(options?.message ?? defaultMessage(type, options?.fileName), { cause: options?.cause });\n    this.name = \"UploadError\";\n    this.type = type;\n    this.fileName = options?.fileName;\n    this.status = options?.status;\n    this.retryAfterMs = options?.retryAfterMs;\n  }\n}\n\n/**\n * Wrap anything thrown by a consumer callback. An existing `UploadError` passes through unchanged\n * so a deliberate `throw new UploadError(...)` keeps its type and message. Otherwise a thrown\n * `Error`'s own message is preferred over the generic default — consumers throw from\n * `requestUpload` precisely to say something specific (\"Files cannot be larger than 10 MB\"), and\n * replacing that with \"Could not start the upload\" would throw away the only useful part.\n */\nexport const toUploadError = (\n  error: unknown,\n  type: UploadErrorType,\n  options?: UploadErrorOptions,\n): UploadError => {\n  if (error instanceof UploadError) return error;\n  const message =\n    options?.message ?? (error instanceof Error && error.message ? error.message : undefined);\n  return new UploadError(type, { ...options, message, cause: error });\n};\n","import { UploadError } from \"./errors\";\nimport type { UploadPartFn } from \"./uploader.types\";\n\n/** Default first-retry window; see `UploaderConfig.retryBaseMs`. */\nexport const RETRY_BASE_MS = 500;\n/** Default backoff ceiling; see `UploaderConfig.retryCapMs`. */\nexport const RETRY_CAP_MS = 8_000;\n/** A server's `Retry-After` is honored up to this; beyond it we would stall the whole queue. */\nexport const MAX_RETRY_AFTER_MS = 30_000;\n/** Default stall budget; see `UploaderConfig.stallTimeoutMs`. */\nexport const STALL_TIMEOUT_MS = 60_000;\n\nlet keyCounter = 0;\n\n/**\n * A stable client-side identity for an upload. Used only for React keys — it is never transmitted,\n * persisted, or compared against anything server-side, so it needs uniqueness within the page and\n * nothing more.\n *\n * A plain counter rather than `crypto.randomUUID()`: that is unavailable outside a secure context\n * (plain HTTP on a LAN address is enough to make it `undefined`), and a random id would differ\n * between a server render and hydration, remounting every rehydrated row. A counter is deterministic,\n * so the same sequence of constructions yields the same keys on both sides.\n *\n * `useId` can't serve here — it is a hook returning one id per component, while uploads are\n * constructed from event handlers, and the models deliberately don't depend on React at all.\n */\nexport const nextUploadKey = (): string => `upload-${++keyCounter}`;\n\n/**\n * Whether two `File`s should be treated as the same selection: reference identity first, then\n * name + size + type.\n *\n * The structural fallback matters for interop — it is exactly the comparison\n * `@zag-js/file-utils`' `isFileEqual` uses, so a design system's file list and the uploader's agree\n * about what \"the same file\" means. Reference equality alone would churn whenever the selection layer\n * hands back re-created `File` objects (Zag's `transformFiles` does this).\n */\nexport const isSameFile = (a: File, b: File): boolean =>\n  a === b || (a.name === b.name && a.size === b.size && a.type === b.type);\n\n/**\n * The filename's extension, lowercased, or `\"\"` when there isn't one.\n *\n * Deliberately reports what the name says and normalizes nothing — the reference implementation\n * rewrote `jpeg` to `jpg` for one backend's content-type table, which is an application concern.\n */\nexport const getFileExtension = (fileName: string): string => {\n  const dot = fileName.lastIndexOf(\".\");\n  if (dot <= 0 || dot === fileName.length - 1) return \"\";\n  return fileName.slice(dot + 1).toLowerCase();\n};\n\n/**\n * Whether another attempt against the same signed URL could plausibly succeed.\n *\n * - `0` — no response at all: network error, or our own stall abort\n * - `408` — server-side request timeout\n * - `429` — rate limited (`Retry-After` honored when present)\n * - `5xx` except `501` — transient server failure\n *\n * Everything else is fatal, most importantly `403`: an expired or mismatched presign fails\n * identically forever, so retrying it only delays the error.\n */\nexport const isRetryableStatus = (status: number): boolean =>\n  status === 0 || status === 408 || status === 429 || (status >= 500 && status !== 501);\n\n/** `Retry-After` as ms; accepts delta-seconds or an HTTP-date. */\nexport const parseRetryAfter = (header: string | null): number | undefined => {\n  if (!header) return undefined;\n  const trimmed = header.trim();\n  if (!trimmed) return undefined;\n  const seconds = Number(trimmed);\n  if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n  const date = Date.parse(trimmed);\n  return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n};\n\nexport interface RetryDelayOptions {\n  baseMs?: number;\n  capMs?: number;\n  retryAfterMs?: number;\n}\n\n/**\n * Equal-jitter exponential backoff: half the window fixed, half random. Full jitter can return ~0ms,\n * which just re-hammers a server that is already struggling; a fixed delay synchronizes every part\n * of every file into a thundering herd.\n *\n * With the defaults (base 500, factor 2, cap 8000) a part's three retry delays are 250–500ms,\n * 500–1000ms and 1000–2000ms, so backoff totals at most 3.5s.\n *\n * @param attempt 1-based number of the attempt that just failed.\n */\nexport const retryDelayMs = (attempt: number, options?: RetryDelayOptions): number => {\n  if (options?.retryAfterMs !== undefined) {\n    return Math.min(options.retryAfterMs, MAX_RETRY_AFTER_MS);\n  }\n  const base = options?.baseMs ?? RETRY_BASE_MS;\n  const window = Math.min(options?.capMs ?? RETRY_CAP_MS, base * 2 ** Math.max(0, attempt - 1));\n  return Math.round(window / 2 + Math.random() * (window / 2));\n};\n\n/**\n * The default part transport: a plain `PUT` of the part's blob to its signed URL.\n *\n * XHR rather than `fetch` because `fetch` still cannot report upload progress in most browsers.\n *\n * Every terminal path settles the promise — `onload`, `onerror`, `ontimeout`, `onabort`, and the\n * stall timer. The reference implementation registered only `onload` and `onerror`, so an aborted\n * part's promise never settled: the part stayed `UPLOADING` forever, permanently holding a\n * concurrency slot, and enough cancellations deadlocked the whole uploader.\n */\nexport const xhrPutUpload: UploadPartFn = (signal, part, onProgress) =>\n  new Promise<void>((resolve, reject) => {\n    const fileName = part.file.name;\n\n    if (signal.aborted) {\n      reject(new UploadError(\"ABORTED\", { fileName }));\n      return;\n    }\n\n    const stallMs = part.file.uploader.stallTimeoutMs;\n    const xhr = new XMLHttpRequest();\n    let stallTimer: ReturnType<typeof setTimeout> | undefined;\n    let stalled = false;\n\n    const onAbort = (): void => xhr.abort();\n\n    const cleanup = (): void => {\n      clearTimeout(stallTimer);\n      signal.removeEventListener(\"abort\", onAbort);\n    };\n\n    const armStall = (): void => {\n      if (!stallMs) return;\n      clearTimeout(stallTimer);\n      stallTimer = setTimeout(() => {\n        stalled = true;\n        xhr.abort();\n      }, stallMs);\n    };\n\n    xhr.onload = (): void => {\n      cleanup();\n      // any 2xx, not just 200 — object stores answer 200/201/204 depending on the route, and the\n      // reference treated 204 as a failure\n      if (xhr.status >= 200 && xhr.status < 300) {\n        resolve();\n        return;\n      }\n      reject(\n        new UploadError(\"PART\", {\n          fileName,\n          status: xhr.status,\n          retryAfterMs: parseRetryAfter(xhr.getResponseHeader(\"Retry-After\")),\n          message: `Part ${part.index + 1} of '${fileName}' failed: ${xhr.status} ${xhr.statusText}.`,\n        }),\n      );\n    };\n\n    xhr.onerror = (): void => {\n      cleanup();\n      reject(\n        new UploadError(\"PART\", {\n          fileName,\n          status: 0,\n          message: `Network error uploading part ${part.index + 1} of '${fileName}'.`,\n        }),\n      );\n    };\n\n    xhr.ontimeout = (): void => {\n      cleanup();\n      reject(\n        new UploadError(\"PART\", {\n          fileName,\n          status: 0,\n          message: `Timed out uploading part ${part.index + 1} of '${fileName}'.`,\n        }),\n      );\n    };\n\n    xhr.onabort = (): void => {\n      cleanup();\n      // a stall abort is our own doing and is retryable; a signal abort is cancellation\n      reject(\n        stalled\n          ? new UploadError(\"PART\", {\n              fileName,\n              status: 0,\n              message: `Part ${part.index + 1} of '${fileName}' stalled for ${stallMs}ms.`,\n            })\n          : new UploadError(\"ABORTED\", { fileName }),\n      );\n    };\n\n    signal.addEventListener(\"abort\", onAbort, { once: true });\n    xhr.open(\"PUT\", part.url);\n    xhr.upload.onprogress = (event): void => {\n      armStall();\n      onProgress(event.loaded);\n    };\n    // Deliberately no headers. Content-Length is a forbidden header: the browser sets it from the\n    // blob and a script cannot override it. That is exactly why part sizes must come from the\n    // server rather than be derived client-side — see UploadPart.size.\n    armStall();\n    xhr.send(part.blob);\n  });\n","import { action, makeObservable, observable } from \"mobx\";\nimport type { UploadError } from \"./errors\";\nimport type { UploaderModel } from \"./uploader.model\";\nimport type { CompletedUploadConfig, FileStatus, UploadLike, UploadValue } from \"./uploader.types\";\nimport { getFileExtension, nextUploadKey } from \"./uploader.util\";\n\n/**\n * An upload that already exists server-side, rehydrated from the controlled `value` — no blob, no\n * parts, nothing in flight.\n *\n * Implements `UploadLike` with literal answers (`status` is always `\"COMPLETED\"`, `progress` always\n * 100) so list UIs, `values`, `invalid` and `clear` treat it exactly like a `FileModel` and no\n * `instanceof` branch is needed anywhere.\n *\n * `name` is carried explicitly. The reference implementation derived it from the identifier with\n * `uploadKey.replace(/^.*[\\\\/]/, \"\")`, which only worked because that backend's client-facing id was\n * the bucket key; against an opaque id it renders the id itself and yields no extension.\n */\nexport class CompletedUploadModel implements UploadLike {\n  readonly uploader: UploaderModel;\n  readonly config: CompletedUploadConfig;\n  /** Stable client identity for React keys; distinct from the server's `uploadId`. */\n  readonly key: string = nextUploadKey();\n\n  /** The parent owns this: a `value` update may rename an upload the uploader never uploaded. */\n  private currentName: string;\n\n  get uploadId(): string {\n    return this.config.id;\n  }\n\n  get name(): string {\n    return this.currentName;\n  }\n\n  get extension(): string {\n    return getFileExtension(this.name);\n  }\n\n  get status(): FileStatus {\n    return \"COMPLETED\";\n  }\n\n  get progress(): number {\n    return 100;\n  }\n\n  get error(): UploadError | undefined {\n    return undefined;\n  }\n\n  get value(): UploadValue {\n    return { id: this.uploadId, name: this.name };\n  }\n\n  constructor(uploader: UploaderModel, config: CompletedUploadConfig) {\n    this.uploader = uploader;\n    this.config = config;\n    this.currentName = config.name;\n\n    makeObservable<this, \"currentName\">(this, {\n      currentName: observable,\n      setName: action,\n    });\n  }\n\n  setName(name: string): void {\n    this.currentName = name;\n  }\n\n  remove(): void {\n    this.uploader.removeUpload(this);\n  }\n\n  /** No-op: there is nothing to arm. Present so the `Upload` union is uniform. */\n  activate(): void {}\n\n  /** No-op: no blob, no request, no timers. */\n  dispose(): void {}\n}\n","import { createContext, useContext } from \"react\";\nimport type { UploaderModel } from \"./uploader.model\";\n\n/**\n * What the parts below `<Uploader.Root>` need: the model, plus the one thing only the root can\n * provide — a handle on its hidden `<input type=\"file\">`. Opening the file dialog is a DOM\n * capability, not model state, so it rides the context rather than the model.\n */\nexport interface UploaderContextValue {\n  uploader: UploaderModel;\n  /** Opens the root's hidden file input. Must be called from within a user gesture. */\n  openFileDialog: () => void;\n}\n\nexport const uploaderContext = createContext<UploaderContextValue | undefined>(undefined);\n\nexport const useUploaderContext = (): UploaderContextValue => {\n  const context = useContext(uploaderContext);\n  if (!context) {\n    throw new Error(\n      \"Uploader context not available. Are you within the <Uploader.Root /> component?\",\n    );\n  }\n  return context;\n};\n\nexport const UploaderProvider = uploaderContext.Provider;\n","import {\n  type ChangeEvent,\n  type FC,\n  type InputHTMLAttributes,\n  type ReactNode,\n  useCallback,\n  useMemo,\n  useRef,\n} from \"react\";\nimport { UploaderProvider } from \"../uploader.context\";\nimport type { UploaderModel } from \"../uploader.model\";\n\nexport interface UploaderRootProps {\n  uploader: UploaderModel;\n  /**\n   * Escape hatch for the hidden file input (`id`, `name`, `form`, …). Spread *before* the library's\n   * own attributes. `accept`, `multiple` and `capture` come from `uploader.config` so there is one\n   * place to set them, and `type`/`onChange` are the component's own.\n   */\n  inputProps?: Omit<\n    InputHTMLAttributes<HTMLInputElement>,\n    \"type\" | \"value\" | \"onChange\" | \"accept\" | \"multiple\" | \"capture\"\n  >;\n  children?: ReactNode;\n}\n\n/**\n * Owns the hidden `<input type=\"file\">` — and therefore the only way to open the file dialog, which\n * it publishes on the context as `openFileDialog` — and provides the model to everything below.\n *\n * Renders **no wrapper element**: an uploader has no structural DOM requirement the way a virtualized\n * table's scroll viewport does, so a div here would only be a box you have to style around. Wrap the\n * children in your own container.\n */\nexport const UploaderRoot: FC<UploaderRootProps> = ({ uploader, inputProps, children }) => {\n  const inputRef = useRef<HTMLInputElement>(null);\n\n  // A stable callback over a ref, not state set from a ref callback: children render on the first\n  // pass and a remount costs no extra render.\n  const openFileDialog = useCallback(() => inputRef.current?.click(), []);\n  const context = useMemo(() => ({ uploader, openFileDialog }), [uploader, openFileDialog]);\n\n  const onInputChange = useCallback(\n    (e: ChangeEvent<HTMLInputElement>) => {\n      const { files } = e.target;\n      if (files?.length) uploader.addFiles(files);\n      // clear the input so picking the same file again still fires a change event\n      e.target.value = \"\";\n    },\n    [uploader],\n  );\n\n  return (\n    <UploaderProvider value={context}>\n      <input\n        {...inputProps}\n        ref={inputRef}\n        type=\"file\"\n        accept={uploader.config.accept}\n        multiple={uploader.config.multiple}\n        capture={uploader.config.capture}\n        onChange={onInputChange}\n        // a nameless file input in the tab order is worse than no input at all; the control that\n        // opens the dialog is yours, and it is a real button\n        tabIndex={-1}\n        aria-hidden=\"true\"\n        style={{ display: \"none\" }}\n      />\n      {children}\n    </UploaderProvider>\n  );\n};\n","import { observer } from \"mobx-react-lite\";\nimport { type FC, Fragment, type ReactNode } from \"react\";\nimport { useUploaderContext } from \"../uploader.context\";\nimport type { Upload } from \"../uploader.types\";\n\nexport interface UploaderUploadsProps {\n  /** Renders one upload — in-flight and already-completed alike. */\n  children: (upload: Upload) => ReactNode;\n}\n\n/**\n * Renders every upload through your render prop, in display order, keyed on `upload.key`.\n *\n * Emits no DOM element of its own, so it drops straight into whatever list markup you already have.\n * Reading `uploader.uploads` yourself inside an `observer` is equivalent — this only saves reaching\n * for the context and remembering which identity is the stable one.\n */\nexport const UploaderUploads: FC<UploaderUploadsProps> = observer(({ children }) => {\n  const { uploader } = useUploaderContext();\n\n  return (\n    <>\n      {uploader.uploads.map((upload) => (\n        <Fragment key={upload.key}>{children(upload)}</Fragment>\n      ))}\n    </>\n  );\n});\n","import { UploaderRoot } from \"./uploader-root\";\nimport { UploaderUploads } from \"./uploader-uploads\";\n\n/**\n * Compound namespace for the uploader skeleton. Consumers compose these into their own closed\n * component (styles + defaults captured once), e.g. `<Uploader.Root><Uploader.Uploads>…`.\n */\nexport const Uploader = {\n  Root: UploaderRoot,\n  Uploads: UploaderUploads,\n};\n","import { action, computed, makeObservable, observable } from \"mobx\";\nimport { UploadError } from \"./errors\";\nimport type { FileModel } from \"./file.model\";\nimport type { PartConfig, PartStatus } from \"./uploader.types\";\nimport { retryDelayMs, xhrPutUpload } from \"./uploader.util\";\n\n/**\n * One part of a multipart upload: a slice of the file plus the URL it was signed for.\n *\n * A part is a **single-attempt unit**. It does not loop over retries itself — a retryable failure\n * parks it in `WAITING`, releasing its concurrency slot for the duration of the backoff, and a timer\n * flips it back to `QUEUED` for the uploader's scheduler to pick up. The retry \"loop\" is therefore\n * the scheduler, which is what makes backoff free of concurrency cost and every intermediate state\n * inspectable. (The reference implementation slept inside `upload()` with the status still\n * `UPLOADING`, so a part waiting 3.3s held one of only four slots.)\n */\nexport class PartModel {\n  readonly file: FileModel;\n  readonly config: PartConfig;\n\n  status: PartStatus = \"QUEUED\";\n  /**\n   * Bytes confirmed on the wire. Bytes rather than a percentage: with exact server-supplied part\n   * sizes the parts are not equal-sized, so only a byte-weighted roll-up reports the file's real\n   * progress.\n   */\n  loaded = 0;\n  /** 1-based count of attempts started. */\n  attempt = 0;\n  error: UploadError | undefined = undefined;\n\n  // Control flow only — nothing derives from these, so they stay out of the observable map.\n  private controller: AbortController | undefined;\n  private retryTimer: ReturnType<typeof setTimeout> | undefined;\n\n  get index(): number {\n    return this.config.index;\n  }\n\n  get url(): string {\n    return this.config.url;\n  }\n\n  get blob(): Blob {\n    return this.config.blob;\n  }\n\n  get size(): number {\n    return this.config.blob.size;\n  }\n\n  /** 0–100 for this part alone. Zero-guarded: a zero-byte part is 0% until it completes. */\n  get progress(): number {\n    if (this.status === \"COMPLETED\") return 100;\n    if (!this.size) return 0;\n    return Math.min(100, Math.floor((this.loaded / this.size) * 100));\n  }\n\n  constructor(file: FileModel, config: PartConfig) {\n    this.file = file;\n    this.config = config;\n\n    makeObservable<this, \"settle\" | \"wait\" | \"queue\" | \"setLoaded\">(this, {\n      status: observable,\n      loaded: observable,\n      attempt: observable,\n      error: observable.ref,\n\n      progress: computed,\n\n      start: action,\n      requeue: action,\n      abort: action,\n      dispose: action,\n      settle: action,\n      wait: action,\n      queue: action,\n      setLoaded: action,\n    });\n  }\n\n  /**\n   * @internal Scheduler entry point: `QUEUED` -> `UPLOADING`. Sets the status synchronously, before\n   * the first await, so the scheduler's slot accounting is correct the moment this returns.\n   */\n  start(): void {\n    if (this.status !== \"QUEUED\") return;\n    this.status = \"UPLOADING\";\n    this.attempt++;\n    this.error = undefined;\n    this.loaded = 0;\n    const controller = new AbortController();\n    this.controller = controller;\n    void this.attemptUpload(controller);\n  }\n\n  /** @internal `FAILED` -> `QUEUED` with the attempt counter reset, for `file.retry()`. */\n  requeue(): void {\n    if (this.status !== \"FAILED\") return;\n    this.status = \"QUEUED\";\n    this.attempt = 0;\n    this.error = undefined;\n    this.loaded = 0;\n  }\n\n  /**\n   * @internal Abandon the current attempt and become eligible again. Used when the file fails or the\n   * uploader is parked. Object-store part PUTs are not range-resumable, so the part restarts from 0.\n   */\n  abort(): void {\n    clearTimeout(this.retryTimer);\n    this.retryTimer = undefined;\n    this.controller?.abort();\n    this.controller = undefined;\n    if (this.status === \"UPLOADING\" || this.status === \"WAITING\") {\n      this.status = \"QUEUED\";\n      this.loaded = 0;\n    }\n  }\n\n  /** Release every resource. Leaves the part in a resumable status; pairs with the uploader's `activate`. */\n  dispose(): void {\n    this.abort();\n  }\n\n  /**\n   * Exactly one attempt.\n   *\n   * `controller` is captured as a parameter rather than read off `this`: after a dispose/activate\n   * cycle a newer attempt owns `this.controller`, and this continuation has to recognize itself as\n   * stale instead of clobbering the new one's state.\n   */\n  private async attemptUpload(controller: AbortController): Promise<void> {\n    const { config } = this.file.uploader;\n\n    try {\n      await (config.uploadPart ?? xhrPutUpload)(controller.signal, this, (loaded) => {\n        if (!controller.signal.aborted) this.setLoaded(loaded);\n      });\n      // aborted mid-flight: abort() has already written a resumable status, so writing here would\n      // race it and resurrect a part the scheduler has moved on from\n      if (controller.signal.aborted) return;\n      this.settle(\"COMPLETED\");\n    } catch (e: unknown) {\n      if (controller.signal.aborted) return;\n\n      const error =\n        e instanceof UploadError\n          ? e\n          : new UploadError(\"PART\", { cause: e, fileName: this.file.name });\n\n      if (error.type === \"ABORTED\") return;\n\n      const retryable = config.isRetryable\n        ? config.isRetryable(error)\n        : this.file.uploader.isRetryableError(error);\n\n      if (!retryable || this.attempt >= this.file.uploader.maxPartAttempts) {\n        this.settle(\"FAILED\", error);\n      } else {\n        this.wait(error);\n      }\n    } finally {\n      // whatever happened, slots may have freed up\n      this.file.uploader.pump();\n    }\n  }\n\n  /** `UPLOADING` -> `WAITING`. Releases the part slot; the timer re-queues. */\n  private wait(error: UploadError): void {\n    this.status = \"WAITING\";\n    this.error = error;\n    this.loaded = 0;\n    this.controller = undefined;\n    const { config } = this.file.uploader;\n    const delay = retryDelayMs(this.attempt, {\n      baseMs: config.retryBaseMs,\n      capMs: config.retryCapMs,\n      retryAfterMs: error.retryAfterMs,\n    });\n    this.retryTimer = setTimeout(() => this.queue(), delay);\n  }\n\n  /** `WAITING` -> `QUEUED`, from the backoff timer. */\n  private queue(): void {\n    this.retryTimer = undefined;\n    if (this.status !== \"WAITING\") return;\n    this.status = \"QUEUED\";\n    this.file.uploader.pump();\n  }\n\n  private settle(status: \"COMPLETED\" | \"FAILED\", error?: UploadError): void {\n    this.status = status;\n    this.error = error;\n    // progress events can under-report; a completed part is fully loaded by definition\n    this.loaded = status === \"COMPLETED\" ? this.size : 0;\n    this.controller = undefined;\n  }\n\n  private setLoaded(loaded: number): void {\n    this.loaded = Math.min(loaded, this.size);\n  }\n}\n","import { action, computed, makeObservable, observable } from \"mobx\";\nimport { toUploadError, UploadError } from \"./errors\";\nimport { PartModel } from \"./part.model\";\nimport type { UploaderModel } from \"./uploader.model\";\nimport type {\n  FileConfig,\n  FileStatus,\n  UploadLike,\n  UploadRequestResult,\n  UploadValue,\n} from \"./uploader.types\";\nimport { getFileExtension, nextUploadKey } from \"./uploader.util\";\n\n/**\n * One local `File` being uploaded: its server identity, its parts, and its phase.\n *\n * `status` is **explicit observable state**, not a derivation from the parts. `PENDING` and\n * `REQUESTING` both have zero parts, `COMPLETING` and `COMPLETED` both have all parts complete, and\n * `COMPLETED` has to be sticky because it is what gets emitted into the consumer's form value — a\n * derivation would un-complete a file the moment any part model was touched. Parts still *drive* the\n * status, but through the uploader's scheduler rather than through a getter.\n */\nexport class FileModel implements UploadLike {\n  readonly uploader: UploaderModel;\n  readonly config: FileConfig;\n  /** Stable client identity for React keys; distinct from the server's `uploadId`. */\n  readonly key: string = nextUploadKey();\n\n  status: FileStatus = \"PENDING\";\n  parts: PartModel[] = [];\n  uploadId: string | undefined = undefined;\n  error: UploadError | undefined = undefined;\n\n  /** The server's canonical name, once known. Falls back to the local `File.name`. */\n  private serverName: string | undefined = undefined;\n\n  // Control flow only — nothing derives from these.\n  private controller: AbortController | undefined;\n  private completeRequested = false;\n  private objectUrlValue: string | undefined;\n\n  get file(): File {\n    return this.config.file;\n  }\n\n  get size(): number {\n    return this.config.file.size;\n  }\n\n  get type(): string {\n    return this.config.file.type;\n  }\n\n  get isImage(): boolean {\n    return this.type.startsWith(\"image/\");\n  }\n\n  get isVideo(): boolean {\n    return this.type.startsWith(\"video/\");\n  }\n\n  get previewable(): boolean {\n    return this.isImage || this.isVideo;\n  }\n\n  /**\n   * A blob URL for previewing the file, minted on first read and revoked by `dispose`.\n   *\n   * Deliberately **not** a `computed`. A computed's body must be pure, and `URL.createObjectURL`\n   * allocates a document-scoped handle; worse, computeds suspend when unobserved and recompute on\n   * the next read, so a computed here mints a fresh blob URL every time a preview unmounts and\n   * remounts and leaks the previous one for the page's lifetime. This is a plain getter over\n   * readonly, non-observable state memoized into a plain field, so it can never invalidate: it mints\n   * at most once per activate/dispose cycle and `dispose` revokes exactly what was minted.\n   *\n   * `undefined` rather than `\"\"` for non-previewable files, so consumers don't render `<img src=\"\">`\n   * (which requests the current page).\n   */\n  get objectUrl(): string | undefined {\n    if (!this.previewable) return undefined;\n    this.objectUrlValue ??= URL.createObjectURL(this.config.file);\n    return this.objectUrlValue;\n  }\n\n  get name(): string {\n    return this.serverName ?? this.config.file.name;\n  }\n\n  get extension(): string {\n    return getFileExtension(this.name);\n  }\n\n  get value(): UploadValue | undefined {\n    if (this.status !== \"COMPLETED\" || this.uploadId === undefined) return undefined;\n    return { id: this.uploadId, name: this.name };\n  }\n\n  get queuedParts(): PartModel[] {\n    return this.parts.filter((part) => part.status === \"QUEUED\");\n  }\n\n  get activeParts(): PartModel[] {\n    return this.parts.filter((part) => part.status === \"UPLOADING\");\n  }\n\n  get waitingParts(): PartModel[] {\n    return this.parts.filter((part) => part.status === \"WAITING\");\n  }\n\n  get completedParts(): PartModel[] {\n    return this.parts.filter((part) => part.status === \"COMPLETED\");\n  }\n\n  get failedParts(): PartModel[] {\n    return this.parts.filter((part) => part.status === \"FAILED\");\n  }\n\n  /** Vacuously true for a zero-part file, which is how a zero-byte upload completes. */\n  get partsComplete(): boolean {\n    return this.parts.every((part) => part.status === \"COMPLETED\");\n  }\n\n  /** Bytes confirmed on the wire across every part. */\n  get loaded(): number {\n    return this.parts.reduce((sum, part) => sum + part.loaded, 0);\n  }\n\n  /**\n   * 0–100, weighted by **bytes** rather than by part count — with exact server-supplied sizes the\n   * parts are unequal, so averaging their percentages misreports. Zero-guarded: the reference\n   * divided by `parts.length` and rendered `NaN` for the entire pre-signing phase.\n   */\n  get progress(): number {\n    if (this.status === \"COMPLETED\") return 100;\n    if (!this.size) return 0;\n    return Math.min(100, Math.floor((this.loaded / this.size) * 100));\n  }\n\n  /** Whether this file has reached a terminal status. */\n  get settled(): boolean {\n    return this.status === \"COMPLETED\" || this.status === \"FAILED\";\n  }\n\n  constructor(uploader: UploaderModel, config: FileConfig) {\n    this.uploader = uploader;\n    this.config = config;\n\n    makeObservable<this, \"serverName\" | \"applyRequestResult\">(this, {\n      status: observable,\n      // shallow: PartModels are observable in their own right, and the array is replaced wholesale\n      // exactly once (applyRequestResult) rather than mutated in place\n      parts: observable.shallow,\n      uploadId: observable.ref,\n      serverName: observable.ref,\n      error: observable.ref,\n\n      name: computed,\n      extension: computed,\n      value: computed,\n      queuedParts: computed,\n      activeParts: computed,\n      waitingParts: computed,\n      completedParts: computed,\n      failedParts: computed,\n      partsComplete: computed,\n      loaded: computed,\n      progress: computed,\n      settled: computed,\n\n      startRequest: action,\n      startComplete: action,\n      complete: action,\n      fail: action,\n      applyRequestResult: action,\n\n      retry: action.bound,\n      remove: action.bound,\n      activate: action.bound,\n      dispose: action.bound,\n    });\n  }\n\n  /**\n   * @internal Scheduler entry point: `PENDING` -> `REQUESTING`. Sets the status synchronously,\n   * before the first await, so the scheduler's slot accounting is correct the moment this returns.\n   */\n  startRequest(): void {\n    if (this.status !== \"PENDING\") return;\n    this.status = \"REQUESTING\";\n    this.error = undefined;\n    const controller = new AbortController();\n    this.controller = controller;\n    void this.runRequest(controller);\n  }\n\n  /** @internal Scheduler entry point: `UPLOADING` -> `COMPLETING`. Fires `completeUpload` once. */\n  startComplete(): void {\n    if (this.status !== \"UPLOADING\" || this.completeRequested) return;\n    this.status = \"COMPLETING\";\n    this.completeRequested = true;\n    const controller = new AbortController();\n    this.controller = controller;\n    void this.runComplete(controller);\n  }\n\n  /** @internal Terminal success. */\n  complete(): void {\n    this.status = \"COMPLETED\";\n    this.error = undefined;\n    this.controller = undefined;\n  }\n\n  /** @internal Terminal failure. */\n  fail(error: UploadError): void {\n    this.status = \"FAILED\";\n    this.error = error;\n    this.controller?.abort();\n    this.controller = undefined;\n    for (const part of this.parts) part.abort();\n    if (error.dev) {\n      // a broken integration contract, not a runtime condition — make it impossible to miss\n      console.error(error);\n    }\n    this.uploader.reportError(error, this);\n  }\n\n  /**\n   * Try a failed upload again. Re-signs when no id was ever obtained, re-issues just the completion\n   * call when that is what failed, and otherwise re-queues the failed parts.\n   */\n  retry(): void {\n    if (this.status !== \"FAILED\") return;\n    this.error = undefined;\n\n    if (this.uploadId === undefined) {\n      this.parts = [];\n      this.completeRequested = false;\n      this.status = \"PENDING\";\n    } else if (this.completeRequested) {\n      // the parts all landed; only finalization failed\n      this.completeRequested = false;\n      this.status = \"UPLOADING\";\n    } else {\n      for (const part of this.parts) part.requeue();\n      this.status = \"UPLOADING\";\n    }\n\n    this.uploader.pump();\n  }\n\n  /** Remove this upload from the uploader, cancelling it server-side if it started. */\n  remove(): void {\n    this.uploader.removeUpload(this);\n  }\n\n  /** Re-arm anything `dispose` released. Transient phases are re-issued from the top. */\n  activate(): void {\n    if (this.status === \"REQUESTING\") {\n      this.status = \"PENDING\";\n    } else if (this.status === \"COMPLETING\") {\n      // completeUpload must be idempotent: an aborted request gives no evidence about whether the\n      // server processed it, and re-issuing beats silently dropping the upload\n      this.completeRequested = false;\n      this.status = \"UPLOADING\";\n    }\n  }\n\n  /**\n   * Release every resource and park the work: the in-flight request is aborted, parts are aborted,\n   * and the preview URL is revoked. Transient phases are rolled back to resumable ones rather than\n   * failed, so `activate` can pick the upload back up.\n   */\n  dispose(): void {\n    this.controller?.abort();\n    this.controller = undefined;\n    for (const part of this.parts) part.dispose();\n    if (this.objectUrlValue) {\n      URL.revokeObjectURL(this.objectUrlValue);\n      this.objectUrlValue = undefined;\n    }\n  }\n\n  private async runRequest(controller: AbortController): Promise<void> {\n    try {\n      const result = await this.uploader.config.requestUpload(controller.signal, this);\n      // cancelled during signing: the file may already be gone from the collection, and creating\n      // parts now would upload a file the user removed (a live bug in the reference)\n      if (controller.signal.aborted) return;\n      this.applyRequestResult(result);\n    } catch (e: unknown) {\n      if (controller.signal.aborted) return;\n      this.fail(toUploadError(e, \"REQUEST\", { fileName: this.name }));\n    } finally {\n      this.uploader.pump();\n    }\n  }\n\n  private async runComplete(controller: AbortController): Promise<void> {\n    try {\n      await this.uploader.config.completeUpload?.(controller.signal, this);\n      if (controller.signal.aborted) return;\n      this.complete();\n    } catch (e: unknown) {\n      if (controller.signal.aborted) return;\n      this.fail(toUploadError(e, \"COMPLETE\", { fileName: this.name }));\n    } finally {\n      this.uploader.pump();\n    }\n  }\n\n  /** `REQUESTING` -> `UPLOADING`, slicing the file against the server's exact part sizes. */\n  private applyRequestResult(result: UploadRequestResult): void {\n    const total = result.parts.reduce((sum, part) => sum + part.size, 0);\n    const badSize = result.parts.some((part) => !Number.isSafeInteger(part.size) || part.size <= 0);\n\n    if (total !== this.size || badSize) {\n      this.fail(\n        new UploadError(\"PART_SIZES\", {\n          fileName: result.name || this.name,\n          message:\n            `requestUpload returned ${result.parts.length} part size(s) summing to ${total} ` +\n            `bytes for a ${this.size}-byte file. Every part's \\`size\\` must be the exact byte ` +\n            `length the server signed that URL for: the browser derives Content-Length from the ` +\n            `blob and cannot override it (it is a forbidden header), so a mismatch means the part ` +\n            `upload is rejected with 403 SignatureDoesNotMatch.`,\n        }),\n      );\n      return;\n    }\n\n    this.uploadId = result.id;\n    this.serverName = result.name || undefined;\n\n    // A running offset, not `i * partSize`: the sizes are the server's, they are not necessarily\n    // equal, and an even split silently corrupts every part boundary after the first.\n    let offset = 0;\n    this.parts = result.parts.map((part, index) => {\n      const model = new PartModel(this, {\n        index,\n        url: part.url,\n        blob: this.config.file.slice(offset, offset + part.size),\n      });\n      offset += part.size;\n      return model;\n    });\n\n    this.status = \"UPLOADING\";\n  }\n}\n","import {\n  action,\n  comparer,\n  computed,\n  type IReactionDisposer,\n  makeObservable,\n  observable,\n  reaction,\n  runInAction,\n} from \"mobx\";\nimport { CompletedUploadModel } from \"./completed-upload.model\";\nimport { toUploadError, UploadError } from \"./errors\";\nimport { FileModel } from \"./file.model\";\nimport type { PartModel } from \"./part.model\";\nimport type { Upload, UploaderConfig, UploadValue } from \"./uploader.types\";\nimport { isRetryableStatus, isSameFile, STALL_TIMEOUT_MS } from \"./uploader.util\";\n\n/**\n * Owns the list of uploads and the scheduling of all network work.\n *\n * Scheduling is an explicit, synchronous, idempotent `pump()` action rather than a reaction. The\n * reference implementation used a self-triggering `autorun` that read the part queues and mutated\n * part status, which is a category error — a reaction's contract is \"state to outside world\", not\n * \"state to state\" — and it made the number of reaction passes an emergent property of the code\n * (O(N) passes to dispatch N parts, each recomputing every queue), untestable without leaning on\n * MobX's scheduler, and unable to express two concurrency budgets at once. `pump()` is called from\n * every transition point, so no free slot ever goes unfilled.\n */\nexport class UploaderModel {\n  readonly config: UploaderConfig;\n\n  /** Every upload, in display order: files being uploaded plus rehydrated completed uploads. */\n  uploads: Upload[] = [];\n\n  // Scheduling is a command, not a derivation: these gate and serialize `pump` and are deliberately\n  // NOT observable — nothing derives from them, and observing them would invalidate every\n  // scheduling computed on each pass.\n  private active = false;\n  private pumping = false;\n  private pumpQueued = false;\n  private changeReactionDisposer: IReactionDisposer | undefined;\n\n  // Defaults live in getters, never merged into a defaults object. Plain getters over\n  // non-observable readonly config, so they are not annotated.\n  get concurrency(): number {\n    return this.config.concurrency ?? 4;\n  }\n\n  get maxPendingUploads(): number {\n    return this.config.maxPendingUploads ?? Number.POSITIVE_INFINITY;\n  }\n\n  /**\n   * How many uploads the field holds at once, counting rehydrated ones. Defaults from `multiple`:\n   * unlimited when it is set, `1` when it isn't.\n   */\n  get maxFiles(): number {\n    return this.config.maxFiles ?? (this.config.multiple ? Number.POSITIVE_INFINITY : 1);\n  }\n\n  get maxPartAttempts(): number {\n    return this.config.maxPartAttempts ?? 4;\n  }\n\n  get stallTimeoutMs(): number {\n    return this.config.stallTimeoutMs ?? STALL_TIMEOUT_MS;\n  }\n\n  /** Only the uploads that have a local `File` behind them. */\n  get files(): FileModel[] {\n    return this.uploads.flatMap((upload) => (upload instanceof FileModel ? upload : []));\n  }\n\n  /** Every upload that has reached `COMPLETED`, in display order. */\n  get completedUploads(): Upload[] {\n    return this.uploads.filter((upload) => upload.status === \"COMPLETED\");\n  }\n\n  /** The form value: one `{ id, name }` per completed upload. */\n  get values(): UploadValue[] {\n    return this.uploads.flatMap((upload) => upload.value ?? []);\n  }\n\n  /** Just the ids, for consumers whose field stores bare identifiers. */\n  get ids(): string[] {\n    return this.values.map((value) => value.id);\n  }\n\n  get activeParts(): PartModel[] {\n    return this.files.flatMap((file) => file.activeParts);\n  }\n\n  get queuedParts(): PartModel[] {\n    return this.files.flatMap((file) => file.queuedParts);\n  }\n\n  /** Files whose signing request is in flight; each is a prospective part the pipeline can't see yet. */\n  get requestingFiles(): FileModel[] {\n    return this.files.filter((file) => file.status === \"REQUESTING\");\n  }\n\n  /**\n   * Uploads that exist server-side but are not finished — the count a backend's pending-upload limit\n   * applies to.\n   *\n   * `REQUESTING` files count even though they have no id yet: the request that is in flight is what\n   * *creates* the server-side pending upload, so excluding them would let successive pumps sign past\n   * `maxPendingUploads` while the first requests were still resolving.\n   */\n  get pendingUploads(): FileModel[] {\n    return this.files.filter(\n      (file) =>\n        file.status !== \"COMPLETED\" &&\n        (file.uploadId !== undefined || file.status === \"REQUESTING\"),\n    );\n  }\n\n  /**\n   * Whether the field is at `maxFiles`. Gate your browse control on this rather than keeping your own\n   * count — it is the same number the model refuses additions with, and it counts both kinds of upload.\n   */\n  get full(): boolean {\n    return this.uploads.length >= this.maxFiles;\n  }\n\n  /** How many more uploads will be accepted. `Infinity` when unlimited. */\n  get remainingSlots(): number {\n    return Math.max(0, this.maxFiles - this.uploads.length);\n  }\n\n  /** Whether any upload is still working. Use this instead of peeking at part counts. */\n  get uploading(): boolean {\n    return this.files.some((file) => !file.settled);\n  }\n\n  /** Whether any upload has failed. */\n  get failed(): boolean {\n    return this.uploads.some((upload) => upload.status === \"FAILED\");\n  }\n\n  /** Whether anything is not yet completed — the \"block submit\" flag. */\n  get invalid(): boolean {\n    return this.uploads.some((upload) => upload.status !== \"COMPLETED\");\n  }\n\n  /** Every error currently attached to an upload. */\n  get errors(): UploadError[] {\n    return this.uploads.flatMap((upload) => upload.error ?? []);\n  }\n\n  /** Total bytes across every file being uploaded. */\n  get size(): number {\n    return this.files.reduce((sum, file) => sum + file.size, 0);\n  }\n\n  /** Bytes confirmed on the wire across every file. */\n  get loaded(): number {\n    return this.files.reduce((sum, file) => sum + file.loaded, 0);\n  }\n\n  /**\n   * 0–100 across every non-failed file, weighted by bytes so a 1 GB file doesn't count the same as a\n   * 1 KB one. Zero-guarded — the reference computed `Math.floor(0 / 0)` and returned `NaN` whenever\n   * there was nothing to upload.\n   */\n  get progress(): number {\n    const valid = this.files.filter((file) => file.status !== \"FAILED\");\n    if (!valid.length) return 0;\n    const total = valid.reduce((sum, file) => sum + file.size, 0);\n    if (!total) return valid.every((file) => file.status === \"COMPLETED\") ? 100 : 0;\n    const loaded = valid.reduce((sum, file) => sum + file.loaded, 0);\n    return Math.min(100, Math.floor((loaded / total) * 100));\n  }\n\n  constructor(config: UploaderConfig) {\n    this.config = config;\n\n    makeObservable<\n      this,\n      \"pumpOnce\" | \"settleFiles\" | \"startPendingFiles\" | \"startQueuedParts\" | \"addFile\"\n    >(this, {\n      // shallow: the entries are models that manage their own observability, so only the array's\n      // membership needs tracking. `ref` would be wrong — addFile and removeUpload mutate in place.\n      uploads: observable.shallow,\n\n      files: computed,\n      completedUploads: computed,\n      values: computed,\n      ids: computed,\n      activeParts: computed,\n      queuedParts: computed,\n      requestingFiles: computed,\n      pendingUploads: computed,\n      full: computed,\n      remainingSlots: computed,\n      uploading: computed,\n      failed: computed,\n      invalid: computed,\n      errors: computed,\n      size: computed,\n      loaded: computed,\n      progress: computed,\n\n      addFile: action,\n      addFiles: action.bound,\n      setFiles: action.bound,\n      addCompletedUpload: action.bound,\n      applyValue: action.bound,\n      removeUpload: action.bound,\n      clear: action.bound,\n      retryAll: action.bound,\n      // pump mutates status across many models; as an action the whole pass lands as one\n      // transaction, so observers never see a half-scheduled state and onChange fires once per batch\n      pump: action.bound,\n      pumpOnce: action,\n      settleFiles: action,\n      startPendingFiles: action,\n      startQueuedParts: action,\n    });\n\n    if (config.value) this.applyValue(config.value);\n  }\n\n  /**\n   * (Re)arm the `onChange` reaction and resume scheduling. Idempotent. Pairs with `dispose` —\n   * `useUploader` calls both across effect cycles, so a StrictMode dev remount (mount, cleanup,\n   * mount against the same model) resumes rather than leaving the uploader parked.\n   */\n  activate(): void {\n    this.active = true;\n\n    if (!this.changeReactionDisposer) {\n      this.changeReactionDisposer = reaction(\n        () => this.values,\n        (values) => this.config.onChange?.(values, this),\n        // structural, so a fresh array of fresh objects with the same contents is not a change.\n        // onChange is read off config at fire time, so an inline consumer lambda never re-subscribes.\n        { equals: comparer.structural },\n      );\n    }\n\n    runInAction(() => {\n      for (const upload of this.uploads) upload.activate();\n    });\n    this.pump();\n  }\n\n  /**\n   * Release every resource and park the work: in-flight requests and part uploads are aborted, retry\n   * and stall timers cleared, preview URLs revoked, the `onChange` reaction dropped, and nothing new\n   * is scheduled.\n   *\n   * Uploads are left in the collection with resumable statuses — call `activate` to pick them back\n   * up, or `clear` to abandon them. A destructive dispose would abort every in-flight upload on a\n   * StrictMode dev remount.\n   */\n  dispose(): void {\n    this.active = false;\n    this.changeReactionDisposer?.();\n    this.changeReactionDisposer = undefined;\n    runInAction(() => {\n      for (const upload of this.uploads) upload.dispose();\n    });\n  }\n\n  /**\n   * **Add** files to whatever is already here. Accepts a `FileList` (from an `<input>`'s change event),\n   * an array, or any iterable of `File`.\n   *\n   * Each file goes through `config.validate` in order, after earlier files in the batch have been\n   * added — so a count rule sees them.\n   *\n   * A file already in the list is skipped rather than uploaded twice, matched by {@link isSameFile}\n   * — so `setFiles` and `addFiles` agree about identity, and re-picking the same file can't produce\n   * two uploads of the same bytes, two server-side pending uploads and two entries in the form value.\n   * Skips are reported through `onError` as `UploadError(\"REJECTED\")` rather than dropped silently.\n   *\n   * This is the *delta* API, for a selection layer that reports only what was newly picked — which is\n   * what an `<input type=\"file\">` change event gives you (and what `<Uploader.Root>` uses). If your\n   * selection layer owns the list and hands back the whole thing on every change, use\n   * {@link UploaderModel.setFiles} instead: that also removes files the layer dropped, which this\n   * cannot see.\n   */\n  addFiles(files: FileList | Iterable<File>): void {\n    const list = Array.from(files);\n    if (!list.length) return;\n\n    if (!this.config.multiple) {\n      // single-file mode replaces rather than accumulates: keep only the last pick and drop whatever\n      // was there, of either kind. `clear` is what makes this uniform — the reference called a\n      // `cancel()` that iterated only the in-flight files, so a rehydrated upload survived the\n      // replacement and a single-file uploader ended up holding two. No dedupe check is needed: there\n      // is never anything left to collide with.\n      const last = list[list.length - 1];\n      this.clear();\n      if (last) this.addFile(last);\n    } else {\n      for (const file of list) {\n        if (this.files.some((upload) => isSameFile(upload.file, file))) {\n          this.reportError(\n            new UploadError(\"REJECTED\", {\n              fileName: file.name,\n              message: `'${file.name}' has already been added.`,\n            }),\n          );\n          continue;\n        }\n        this.addFile(file);\n      }\n    }\n\n    this.pump();\n  }\n\n  /**\n   * **Reconcile** the picked files to exactly this set — additions and removals both.\n   *\n   * This is the API for a selection layer that owns the file list and reports the whole thing on every\n   * change rather than a delta. Chakra UI's and Ark UI's `FileUpload` work this way: `onFileAccept`\n   * fires from the machine's `acceptedFiles` binding, so it receives the complete accepted list — and\n   * it fires on deletions too, not just additions. Passing that to `addFiles` would duplicate every\n   * file already present.\n   *\n   * Files are matched to existing uploads by reference, then by name + size + type (see\n   * {@link isSameFile}) — the same identity `@zag-js/file-utils` uses, so both layers agree about what\n   * \"the same file\" is. Matched uploads keep their position and their in-flight state, so a\n   * reconciliation never restarts an upload that is already running. Uploads whose file is absent are\n   * removed, cancelling them server-side if they had started. New files are appended through\n   * `config.validate`.\n   *\n   * Rehydrated completed uploads are left alone: they have no `File`, they aren't part of the selection\n   * layer's list, and they are owned by the controlled `value`. So `setFiles([])` clears the picked\n   * files without discarding uploads that already exist server-side.\n   */\n  setFiles(files: FileList | Iterable<File>): void {\n    const incoming = Array.from(files);\n    // single-file mode keeps the last pick, matching addFiles\n    const wanted = this.config.multiple ? incoming : incoming.slice(-1);\n\n    const existing = this.files;\n    const matched = new Set<FileModel>();\n    const additions: File[] = [];\n\n    for (const file of wanted) {\n      // `matched` guards against one incoming file claiming two existing uploads; the selection layer\n      // normally dedupes, so this only matters for a hand-built list\n      const match = existing.find(\n        (upload) => !matched.has(upload) && isSameFile(upload.file, file),\n      );\n      if (match) {\n        matched.add(match);\n      } else {\n        additions.push(file);\n      }\n    }\n\n    for (const upload of existing) {\n      if (!matched.has(upload)) this.removeUpload(upload);\n    }\n\n    for (const file of additions) {\n      this.addFile(file);\n    }\n\n    this.pump();\n  }\n\n  /** Add a rehydrated upload that already exists server-side. */\n  addCompletedUpload(value: UploadValue): CompletedUploadModel {\n    const upload = new CompletedUploadModel(this, value);\n    this.uploads.push(upload);\n    return upload;\n  }\n\n  /**\n   * Reconcile the controlled value, matching **by `id` only**.\n   *\n   * Only `COMPLETED` uploads take part in the removal diff. `values` contains nothing else, so an\n   * upload still in flight was never in the parent's value and cannot be something the parent\n   * \"dropped\" — which is what keeps a `value` echo from cancelling work in progress, even after the\n   * upload has been signed and therefore has an `uploadId`.\n   *\n   * Being a single action means the diff cannot emit a half-applied list — the reference's bare\n   * `addCompletedUpload` loop ended a MobX batch per push, so `onChange` could fire mid-reconciliation.\n   */\n  applyValue(value: UploadValue[]): void {\n    const byId = new Map(value.map((entry) => [entry.id, entry.name] as const));\n\n    // completed uploads the parent dropped are removed; anything still working is left alone.\n    // `slice()` because removeUpload splices the array we are iterating.\n    for (const upload of this.uploads.slice()) {\n      if (upload.status !== \"COMPLETED\" || upload.uploadId === undefined) continue;\n      if (!byId.has(upload.uploadId)) this.removeUpload(upload);\n    }\n\n    // a rehydrated upload's name belongs to the parent; a picked File's name belongs to the File\n    for (const upload of this.uploads) {\n      if (!(upload instanceof CompletedUploadModel)) continue;\n      const name = byId.get(upload.uploadId);\n      if (name !== undefined && name !== upload.name) upload.setName(name);\n    }\n\n    const present = new Set(this.uploads.flatMap((upload) => upload.uploadId ?? []));\n    for (const [id, name] of byId) {\n      if (!present.has(id)) this.addCompletedUpload({ id, name });\n    }\n\n    this.pump();\n  }\n\n  /**\n   * Remove an upload. A file that started server-side but never completed is cancelled through\n   * `config.cancelUpload`; a completed one is reported to `config.onRemove`.\n   */\n  removeUpload(upload: Upload): void {\n    const index = this.uploads.indexOf(upload);\n    if (index === -1) return;\n    this.uploads.splice(index, 1);\n\n    const value = upload.value;\n    upload.dispose();\n\n    if (\n      upload instanceof FileModel &&\n      upload.uploadId !== undefined &&\n      upload.status !== \"COMPLETED\"\n    ) {\n      // only when there is actually something server-side to abort — the reference called this\n      // unconditionally, including for files that never got an id\n      try {\n        const result = this.config.cancelUpload?.(upload);\n        void Promise.resolve(result).catch((e: unknown) => {\n          this.reportError(toUploadError(e, \"REQUEST\", { fileName: upload.name }), upload);\n        });\n      } catch (e: unknown) {\n        this.reportError(toUploadError(e, \"REQUEST\", { fileName: upload.name }), upload);\n      }\n    } else if (value) {\n      this.config.onRemove?.(value, this);\n    }\n\n    this.pump();\n  }\n\n  /** Remove every upload, of either kind, through the one `removeUpload` path. */\n  clear(): void {\n    // slice() because removeUpload splices the array we are iterating\n    for (const upload of this.uploads.slice()) {\n      this.removeUpload(upload);\n    }\n  }\n\n  /** Retry every failed upload. */\n  retryAll(): void {\n    for (const file of this.files) {\n      if (file.status === \"FAILED\") file.retry();\n    }\n  }\n\n  /** @internal Surface an error to `config.onError`. */\n  reportError(error: UploadError, file?: FileModel): void {\n    this.config.onError?.(error, file, this);\n  }\n\n  /** @internal The default retryable-status policy, overridable via `config.isRetryable`. */\n  isRetryableError(error: UploadError): boolean {\n    if (error.dev) return false;\n    return isRetryableStatus(error.status ?? 0);\n  }\n\n  /**\n   * The module's only scheduling primitive: inspect the current state, fill whatever concurrency\n   * slots are free, return. Synchronous, idempotent, and safe to call at any time — nothing else in\n   * the module starts network work.\n   */\n  pump(): void {\n    // Re-entrancy is impossible through the normal async settle paths, but a synchronous throw out of\n    // a consumer callback could produce one. Coalesce rather than drop.\n    if (this.pumping) {\n      this.pumpQueued = true;\n      return;\n    }\n    this.pumping = true;\n    try {\n      do {\n        this.pumpQueued = false;\n        this.pumpOnce();\n      } while (this.pumpQueued);\n    } finally {\n      this.pumping = false;\n    }\n  }\n\n  private addFile(file: File): void {\n    // The cap is checked before `validate` because it is structural rather than a policy about this\n    // particular file: once the field is full the file is refused whatever else is true of it.\n    if (this.full) {\n      this.reportError(\n        new UploadError(\"REJECTED\", {\n          fileName: file.name,\n          message:\n            this.maxFiles === 1\n              ? `Only one file can be uploaded.`\n              : `No more than ${this.maxFiles} files can be uploaded.`,\n        }),\n        undefined,\n      );\n      return;\n    }\n\n    const rejection = this.config.validate?.(file, this);\n    if (rejection) {\n      // a refused file never enters `uploads`, so it can't show up as a failed row. onError is the\n      // only channel it has — the reference dropped it silently.\n      this.reportError(\n        new UploadError(\"REJECTED\", { fileName: file.name, message: rejection }),\n        undefined,\n      );\n      return;\n    }\n    this.uploads.push(new FileModel(this, { file }));\n  }\n\n  /**\n   * One scheduling pass. The order is load-bearing:\n   *\n   * 1. `settleFiles` advances files whose parts are done (or one of which failed), releasing slots —\n   *    so a freed slot is reused in this same pass rather than the next one.\n   * 2. `startPendingFiles` signs files, creating the parts pass 3 needs.\n   * 3. `startQueuedParts` fills the part slots.\n   */\n  private pumpOnce(): void {\n    if (!this.active) return;\n    this.settleFiles();\n    this.startPendingFiles();\n    this.startQueuedParts();\n  }\n\n  /** `UPLOADING` -> `FAILED` / `COMPLETED` / `COMPLETING`, driven by part aggregates. */\n  private settleFiles(): void {\n    for (const file of this.files) {\n      if (file.status !== \"UPLOADING\") continue;\n\n      // one failed part fails the file; the part's own error carries the status and cause\n      const failed = file.failedParts[0];\n      if (failed) {\n        file.fail(failed.error ?? new UploadError(\"PART\", { fileName: file.name }));\n        continue;\n      }\n\n      // vacuously true for a zero-part file, which is how a zero-byte upload completes\n      if (!file.partsComplete) continue;\n\n      if (this.config.completeUpload) {\n        file.startComplete();\n      } else {\n        file.complete();\n      }\n    }\n  }\n\n  /**\n   * `PENDING` -> `REQUESTING`.\n   *\n   * A file is signed only when the already-signed work can't keep the part pipeline busy without it.\n   * Counting `requestingFiles` is what makes this self-limiting: without that term, a hundred picked\n   * files would all sign on the first tick (none has produced parts yet), which is exactly what a\n   * backend's pending-upload limit rejects. With it, signing stays one step ahead of the pipeline —\n   * so the signing round trip overlaps the tail of the previous file and there is no bubble — while\n   * the number of signed-but-unfinished uploads stays near `concurrency`.\n   */\n  private startPendingFiles(): void {\n    let prospective =\n      this.activeParts.length + this.queuedParts.length + this.requestingFiles.length;\n    let pending = this.pendingUploads.length;\n\n    for (const file of this.files) {\n      if (file.status !== \"PENDING\") continue;\n      if (prospective >= this.concurrency) return;\n      if (pending >= this.maxPendingUploads) return;\n\n      // startRequest sets REQUESTING synchronously, before its first await, so the local accounting\n      // below stays correct without re-reading the computeds\n      file.startRequest();\n      prospective++;\n      pending++;\n    }\n  }\n\n  /**\n   * `QUEUED` -> `UPLOADING`, in `(file order, part index)` order.\n   *\n   * Files drain one at a time rather than interleaving: an upload is worthless to the consumer until\n   * every part lands, so FIFO over indivisible jobs minimizes mean completion time — ten interleaved\n   * files give you nothing usable until the end, ten drained gives you the first at a tenth of the\n   * time. It also finalizes uploads earlier and wastes no bytes when a queued file is cancelled.\n   */\n  private startQueuedParts(): void {\n    let active = this.activeParts.length;\n    if (active >= this.concurrency) return;\n\n    for (const file of this.files) {\n      if (file.status !== \"UPLOADING\") continue;\n      for (const part of file.parts) {\n        if (active >= this.concurrency) return;\n        if (part.status !== \"QUEUED\") continue;\n        part.start();\n        active++;\n      }\n    }\n  }\n}\n","import { useEffect, useRef } from \"react\";\nimport { UploaderModel } from \"./uploader.model\";\nimport type { UploaderConfig } from \"./uploader.types\";\n\nexport const useUploader = (config: UploaderConfig): UploaderModel => {\n  const uploaderRef = useRef<UploaderModel | undefined>(undefined);\n\n  if (uploaderRef.current) {\n    // Refresh the config in place (as useForm does) so inline `onChange` / `requestUpload` lambdas\n    // are always the current ones. The model reads them off `config` at fire time, so its reaction\n    // never re-subscribes and an inline arrow costs nothing.\n    Object.assign(uploaderRef.current.config, config);\n  } else {\n    uploaderRef.current = new UploaderModel(config);\n  }\n\n  // The model's onChange reaction must die with the component or it leaks past unmount.\n  // activate/dispose as an effect pair (not dispose alone) because StrictMode's dev remount runs\n  // cleanup against a model the surviving ref will hand out again — and here dispose only *parks*\n  // the uploads, so activate picks them back up instead of restarting them.\n  useEffect(() => {\n    uploaderRef.current?.activate();\n    return () => uploaderRef.current?.dispose();\n  }, []);\n\n  // Controlled sync, inbound half. Deliberately runs after every commit with no dep array: `value`\n  // is normally a fresh array literal, so an identity dep would never skip anything anyway, and\n  // `applyValue` is a guarded no-op once the id set already matches. Presence of the `value` key —\n  // not its nullishness — decides controlled-ness, so `undefined` still means \"no uploads\" while an\n  // onChange-only uploader keeps owning its own list.\n  useEffect(() => {\n    if (\"value\" in config) {\n      uploaderRef.current?.applyValue(config.value ?? []);\n    }\n  });\n\n  return uploaderRef.current;\n};\n"],"mappings":"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"}