@wcstack/upload 4.4.0 → 4.6.0

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.ja.md CHANGED
@@ -110,6 +110,8 @@ npm install @wcstack/upload
110
110
 
111
111
  観測できるのは `false` へのリセットのみです。`true` 遷移(アップロード開始)は `wcs-upload:trigger-changed` を発火しません。バインディングシステムは `true` を書き込んで開始し、唯一の `false` エッジを観測してコマンドの完了を知ります。これは `@wcstack/fetch` の `trigger` と同じトレードオフです。
112
112
 
113
+ **`files` はアップロードで使い切ります。** `@wcstack/fetch` の `body` と同じです。アップロードが終わると(成功・失敗・中断のどれでも)`files` は `null` に戻り、`wcs-upload:files-changed` がそれを運ぶので、双方向の `files:` 束縛はそのパスも空にします。送り直すには、ファイルをもう一度代入してから `trigger` を書いてください。また `trigger` は書かれたときに `url` と `files` を読みます。1 つの `data-wcs` では、それらの後に書いてください(`url: …; files: …; trigger: …` — 束縛は書いた順に当たります)。ファイルか `url` の無い `trigger`(や `upload()`)は何も送らず、`error` も立てません。4.5 から、要素と理由ごとに 1 回警告します(`[wcs/command-not-ready]`)。4.6 から、`url` がまだ無い自動送信の `<wcs-upload>` に代入したファイルは url を待ち、url が来たときに送られます(以前は送られず、要素が警告していました)。フレームワークの props も書いた順に当たるので、JSX の `files={files} url=…` も `url=… files={files}` と同じく送るようになりました。
114
+
113
115
  ### 3. 宣言的なトリガーターゲット
114
116
 
115
117
  自動トリガーが有効な場合、クリック可能な要素から id で `<wcs-upload>` を参照できます。
@@ -262,7 +264,7 @@ npm install @wcstack/upload
262
264
 
263
265
  ### エラー時の progress
264
266
 
265
- `progress` は各アップロード開始時に `0` へリセットされ、成功時に `100` へ設定されるだけです。HTTP・ネットワーク・abort のエラー時は、**`progress` は意図的に直前の値(例: `70`)のまま据え置かれます**。これは転送がどこで止まったかを UI が示せるようにするためです。失敗の検出には `progress` ではなく `error` / `loading` を使い、古い値を表示したくない場合は `wcs-upload:error` に応じて UI 側で進捗表示をリセット / 非表示にしてください。次回の `upload()` で `progress` は再び `0` にリセットされます。
267
+ `progress` は各アップロード開始時に `0` へリセットされ、成功時に `100` へ設定されるだけです。HTTP・ネットワーク・abort のエラー時は、**`progress` は意図的に直前の値(例: `70`)のまま据え置かれます**。これは転送がどこで止まったかを UI が示せるようにするためです。読み終えてから拒むサーバ(`413 Payload Too Large` など)では、本文をすべて送っているので `progress` は `100` のままです。失敗の検出には `progress` ではなく `error` / `loading` を使い、古い値を表示したくない場合は `wcs-upload:error` に応じて UI 側で進捗表示をリセット / 非表示にしてください。次回の `upload()` で `progress` は再び `0` にリセットされます。
266
268
 
267
269
  ## `:state()` による CSS スタイリング
268
270
 
package/README.md CHANGED
@@ -110,6 +110,8 @@ Use `manual` when you want to choose files first and upload later.
110
110
 
111
111
  Only the `false` reset is observable: the `true` transition (upload start) does **not** dispatch `wcs-upload:trigger-changed`. A binding system writes `true` to start and observes the single `false` edge to know the command finished. This is the same trade-off as `@wcstack/fetch`'s `trigger`.
112
112
 
113
+ **`files` are used up by an upload**, as `@wcstack/fetch`'s `body` is: once an upload settles (success, failure or abort), `files` goes back to `null` and `wcs-upload:files-changed` carries it, so a two-way `files:` binding clears its path too. To retry, assign the files again, then write `trigger`. And `trigger` reads `url` and `files` when it is written: in one `data-wcs`, write it after them (`url: …; files: …; trigger: …` — bindings apply in the order written). A `trigger` (or `upload()`) without files or a `url` sends nothing and sets no `error`; since 4.5 it warns once per element and reason (`[wcs/command-not-ready]`). Since 4.6, files assigned to an automatic `<wcs-upload>` that has no `url` yet wait for it and are sent when it arrives (before, they were not sent and the element warned): a framework's props are applied in the order written too, and `files={files} url=…` in JSX now uploads as `url=… files={files}` does.
114
+
113
115
  ### 3. Declarative trigger target
114
116
 
115
117
  When auto trigger is enabled, a clickable element can point at a `<wcs-upload>` by id.
@@ -262,7 +264,7 @@ On a successful response (status 2xx), both `value` and `status` are updated via
262
264
 
263
265
  ### Progress on error
264
266
 
265
- `progress` is only reset to `0` at the start of each upload and set to `100` on success. On an HTTP, network, or abort error, **`progress` is intentionally left at its last value** (e.g. `70`) so the UI can show where the transfer stopped. Use `error` / `loading` (not `progress`) to detect failure, and reset or hide the progress indicator from your UI in response to `wcs-upload:error` if you do not want a stale value displayed. A subsequent `upload()` resets `progress` back to `0`.
267
+ `progress` is only reset to `0` at the start of each upload and set to `100` on success. On an HTTP, network, or abort error, **`progress` is intentionally left at its last value** (e.g. `70`) so the UI can show where the transfer stopped. A server that refuses the upload after reading it (`413 Payload Too Large`, say) leaves `progress` at `100`: the whole body was sent. Use `error` / `loading` (not `progress`) to detect failure, and reset or hide the progress indicator from your UI in response to `wcs-upload:error` if you do not want a stale value displayed. A subsequent `upload()` resets `progress` back to `0`.
266
268
 
267
269
  ## CSS styling with `:state()`
268
270
 
package/dist/auto.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Importing this module defines the package's custom elements. It exports nothing.
2
+ export {};
package/dist/auto.min.js CHANGED
@@ -1,2 +1,2 @@
1
- const t={triggerAttribute:"data-uploadtarget",tagNames:{upload:"wcs-upload"}};class e{laneKey;policy;_ownerGeneration=0;_latestEpoch=0;_nextOperationId=1;_activeOperationId=void 0;_activeOperationIds=new Set;_queue=[];_inFlightCount=0;_terminal=new Map;_claimedOutcome=new Map;_controllers=new Map;_attempts=new Map;_withSignal;_trace;constructor(t,e,s={}){this.laneKey=t,this.policy=e,this._withSignal=s.withSignal??!1,this._trace=s.trace}get ownerGeneration(){return this._ownerGeneration}get inFlightCount(){return this._inFlightCount}get latestEpoch(){return this._latestEpoch}get activeOperationId(){return this._activeOperationId}begin(){let t;switch(this.policy){case"latest":t=++this._latestEpoch,void 0!==this._activeOperationId&&this._abortController(this._activeOperationId);break;case"exhaust":if(void 0!==this._activeOperationId)return null}const e=this._nextOperationId++,s={operationId:e,ownerGeneration:this._ownerGeneration,laneKey:this.laneKey,policy:this.policy,supersedeEpoch:t};switch(this.policy){case"latest":case"exhaust":this._activeOperationId=e;break;case"queue":this._queue.push(s),void 0===this._activeOperationId&&(this._activeOperationId=e);break;case"overlap":this._activeOperationIds.add(e)}this._inFlightCount+=1,this._attempts.set(e,1);const i=this._makeAttempt(e,1);return void 0!==this._trace&&this._trace({type:"io:operation-started",operationId:e,laneKey:this.laneKey,policy:this.policy}),{ticket:s,attempt:i}}retry(t){if(t.ownerGeneration!==this._ownerGeneration)return null;if(this._terminal.has(t.operationId))return null;const e=this._attempts.get(t.operationId);if(void 0===e)return null;const s=e+1;this._attempts.set(t.operationId,s),this._releaseController(t.operationId);const i=this._makeAttempt(t.operationId,s);return void 0!==this._trace&&this._trace({type:"io:operation-retried",operationId:t.operationId,laneKey:this.laneKey,attempt:s}),i}canCommit(t){if(t.ownerGeneration!==this._ownerGeneration)return!1;const e=this._terminal.get(t.operationId);return(void 0===e||"committing"===e)&&this._isEligible(t)}claimTerminal(t,e){return t.ownerGeneration===this._ownerGeneration&&(!this._terminal.has(t.operationId)&&(!!this._isEligible(t)&&(this._terminal.set(t.operationId,"committing"),this._claimedOutcome.set(t.operationId,e),!0)))}claimedOutcome(t){return this._claimedOutcome.get(t.operationId)}finalize(t){const e=t.operationId,s=this._terminal.get(e);if(void 0!==s&&"committing"!==s)return;let i;i="committing"===s?this._claimedOutcome.get(e)??"stale":"stale",this._terminal.set(e,i),this._claimedOutcome.delete(e),this._releaseController(e),this._attempts.delete(e),this._inFlightCount>0&&(this._inFlightCount-=1),this._advanceBookkeeping(e),void 0!==this._trace&&("stale"===i?this._trace({type:"io:stale-dropped",operationId:e,laneKey:this.laneKey}):this._trace({type:"io:operation-settled",operationId:e,laneKey:this.laneKey,outcome:i}))}signalOf(t){return this._controllers.get(t.operationId)?.signal}abort(t){this._abortController(t.operationId)}abortActive(){void 0!==this._activeOperationId&&this._abortController(this._activeOperationId);for(const t of this._activeOperationIds)this._abortController(t)}disposeOwner(){this._ownerGeneration+=1;for(const t of Array.from(this._controllers.keys()))this._abortController(t),this._releaseController(t);for(const t of Array.from(this._attempts.keys()))this._terminal.has(t)||this._terminal.set(t,"stale"),this._claimedOutcome.delete(t),this._attempts.delete(t),void 0!==this._trace&&this._trace({type:"io:stale-dropped",operationId:t,laneKey:this.laneKey});this._activeOperationId=void 0,this._activeOperationIds.clear(),this._queue.length=0,this._inFlightCount=0}_makeAttempt(t,e){let s;if(this._withSignal&&"function"==typeof AbortController){const e=new AbortController;this._controllers.set(t,e),s=e.signal}return{operationId:t,attempt:e,signal:s}}_isEligible(t){switch(this.policy){case"latest":return t.supersedeEpoch===this._latestEpoch;case"queue":case"exhaust":return this._activeOperationId===t.operationId;case"overlap":return this._activeOperationIds.has(t.operationId)}}_advanceBookkeeping(t){switch(this.policy){case"latest":case"exhaust":this._activeOperationId===t&&(this._activeOperationId=void 0);break;case"queue":{const e=this._queue.filter(e=>e.operationId!==t);this._queue.length=0,this._queue.push(...e),this._activeOperationId=this._queue.length>0?this._queue[0].operationId:void 0;break}case"overlap":this._activeOperationIds.delete(t)}}_abortController(t){const e=this._controllers.get(t);void 0===e||e.signal.aborted||e.abort()}_releaseController(t){this._controllers.delete(t)}}function s(t,e){const s=new Map,i=e=>{const s=t.get(e);return void 0===s?"unknown":s.probe()?"available":"missing"};let r=!0;for(const t of e.required){const e=i(t);s.set(t,e),"available"!==e&&(r=!1)}let n=!0;for(const t of e.optional??[]){const e=i(t);s.set(t,e),"available"!==e&&(n=!1)}const a=r?n?"ready":"degraded":"idle",o=[...e.required,...e.optional??[]],l=o.some(e=>!0===t.get(e)?.requiresSecureContext),c=o.some(e=>!0===t.get(e)?.requiresUserActivation),h=l?!0===globalThis.isSecureContext?"satisfied":"required":"not-applicable",u=c?"required":"not-applicable";return{availability:s,permission:e.permission??"not-applicable",readiness:a,activity:e.activity??"inactive",preconditions:{secureContext:h,userActivation:u},epoch:e.epoch??0,lastError:e.lastError}}function i(t,e){return e.every(e=>"available"===t.availability.get(e))}const r="capability-missing",n="invalid-argument",a="network",o="http-error",l=new Map([["web.xhr",{probe:()=>"function"==typeof globalThis.XMLHttpRequest,compatKey:"api.XMLHttpRequest"}]]);class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:"wcs-upload:response",semantics:"state",getter:t=>t.detail.value},{name:"loading",event:"wcs-upload:loading-changed",semantics:"state"},{name:"progress",event:"wcs-upload:progress",semantics:"state"},{name:"error",event:"wcs-upload:error",semantics:"state"},{name:"status",event:"wcs-upload:response",semantics:"state",getter:t=>t.detail.status},{name:"errorInfo",event:"wcs-upload:error-info-changed",semantics:"state"}],inputs:[{name:"url"},{name:"method"},{name:"fieldName"}],commands:[{name:"upload",async:!0},{name:"abort"}]};static REQUIRED_CAPABILITIES=["web.xhr"];_target;_value=null;_loading=!1;_progress=0;_error=null;_status=0;_errorInfo=null;_xhr=null;_promise=Promise.resolve(null);_lane=new e("upload","latest",{withSignal:!1});_ready=Promise.resolve();constructor(t){super(),this._target=t??this}get ready(){return this._ready}observe(){return this._ready}dispose(){this._lane.disposeOwner(),this.abort()}get value(){return this._value}get loading(){return this._loading}get progress(){return this._progress}get error(){return this._error}get status(){return this._status}get promise(){return this._promise}get errorInfo(){return this._errorInfo}get supported(){return i(this.platformAssessment,c.REQUIRED_CAPABILITIES)}get platformAssessment(){return s(l,{required:c.REQUIRED_CAPABILITIES,activity:this._loading?"active":"inactive",lastError:this._errorInfo??void 0})}_commitStep(t,e){this._lane.canCommit(t)&&e()}_setLoading(t){this._loading=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:loading-changed",{detail:t,bubbles:!0}))}_setProgress(t){this._progress=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:progress",{detail:t,bubbles:!0}))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:error",{detail:t,bubbles:!0})))}setError(t){this._setError(t)}_setResponse(t,e){this._value=t,this._status=e,this._target.dispatchEvent(new CustomEvent("wcs-upload:response",{detail:{value:t,status:e},bubbles:!0}))}_setErrorInfo(t,e,s,i,r){this._commitErrorInfo({code:t,phase:e,recoverable:s,message:i,...void 0===r?{}:{capabilityId:r}})}_commitErrorInfo(t){this._errorInfo!==t&&(this._errorInfo=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:error-info-changed",{detail:t,bubbles:!0})))}abort(){this._xhr&&(this._xhr.abort(),this._xhr=null)}async upload(t,e,s={}){if(!t)return this._setErrorInfo(n,"start",!1,"url is required."),this._setError({message:"url is required."}),null;if(!e||0===e.length)return this._setErrorInfo(n,"start",!1,"files are required."),this._setError({message:"files are required."}),null;const i=this._doUpload(t,e,s);return this._promise=i,i}_doUpload(t,e,s){const n=this.platformAssessment;if(!i(n,c.REQUIRED_CAPABILITIES)){const t=c.REQUIRED_CAPABILITIES.find(t=>"available"!==n.availability.get(t)),e=`Required capability "${t}" is unavailable.`;return this._setErrorInfo(r,"start",!1,e,t),this._setError({message:e}),Promise.resolve(null)}this.abort();const l=this._lane.begin(),{ticket:h}=l;this._commitStep(h,()=>this._setLoading(!0)),this._commitStep(h,()=>{this._setProgress(0),this._commitErrorInfo(null),this._setError(null)});const{method:u="POST",headers:d={},fieldName:p="file"}=s,_=new FormData;for(let t=0;t<e.length;t++)_.append(p,e[t]);return new Promise(e=>{const s=new XMLHttpRequest;this._xhr=s,s.upload.addEventListener("progress",t=>{this._commitStep(h,()=>{t.lengthComputable&&this._setProgress(Math.round(t.loaded/t.total*100))})}),s.addEventListener("load",()=>{if(this._xhr=null,s.status>=200&&s.status<300){if(!this._lane.claimTerminal(h,"success"))return e(null),void this._lane.finalize(h);let t=s.responseText;if((s.getResponseHeader("Content-Type")||"").includes("application/json"))try{t=JSON.parse(s.responseText)}catch{}this._commitStep(h,()=>this._setProgress(100)),this._commitStep(h,()=>this._setResponse(t,s.status)),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(t)}else{if(!this._lane.claimTerminal(h,"error"))return e(null),void this._lane.finalize(h);const t={status:s.status,statusText:s.statusText,body:s.responseText};this._commitStep(h,()=>{this._status=s.status,this._setErrorInfo(o,"execute",!0,`HTTP ${s.status} ${s.statusText}`),this._setError(t)}),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}}),s.addEventListener("error",()=>{if(this._xhr=null,!this._lane.claimTerminal(h,"error"))return e(null),void this._lane.finalize(h);const t="Network error";this._commitStep(h,()=>{this._setErrorInfo(a,"execute",!0,t),this._setError({message:t})}),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}),s.addEventListener("abort",()=>{this._xhr=null,this._lane.claimTerminal(h,"aborted")&&this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}),s.open(u,t);for(const[t,e]of Object.entries(d))s.setRequestHeader(t,e);s.send(_)})}}let h=!1;function u(e){const s=e.target;if(!(s instanceof Element))return;const i=s.closest(`[${t.triggerAttribute}]`);if(!i)return;const r=i.getAttribute(t.triggerAttribute);if(!r)return;const n=customElements.get(t.tagNames.upload),a=document.getElementById(r);if(!(n&&a instanceof n))return;const o=a;o.files&&o.files.length>0&&o.url&&e.preventDefault(),o.upload()}function d(t,e){let s=Object.getPrototypeOf(t);for(;null!==s;){const t=Object.getOwnPropertyDescriptor(s,e);if(void 0!==t)return"function"==typeof t.get||"function"==typeof t.set;s=Object.getPrototypeOf(s)}return!1}const p=new WeakMap;function _(t,e){let s=p.get(t);return void 0===s&&(s=new Map,p.set(t,s)),s.has(e)||s.set(e,t.getAttribute(e)),s.get(e)}function m(t,e,s){null===s?t.removeAttribute(e):t.setAttribute(e,s)}function g(t,e,s){const i=_(t,e);m(t,e,void 0===s?i:null===s?null:String(s))}function f(t,e,s){const i=_(t,e);m(t,e,void 0===s?i:s?"":null)}class b extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...c.wcBindable,properties:[...c.wcBindable.properties,{name:"trigger",event:"wcs-upload:trigger-changed",semantics:"state"},{name:"files",event:"wcs-upload:files-changed",semantics:"state"}],inputs:[{name:"url"},{name:"method"},{name:"fieldName"},{name:"multiple"},{name:"maxSize"},{name:"accept"},{name:"manual"},{name:"files"},{name:"trigger"}]};static get observedAttributes(){return["url"]}_core;_files=null;_trigger=!1;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new c(this),this._internals=this._initInternals(),this._wireStates({"wcs-upload:loading-changed":t=>({loading:!0===t}),"wcs-upload:error":t=>({error:null!=t})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const t=this.attachInternals();return t.states.add("wcs-probe"),t.states.delete("wcs-probe"),t}catch{return null}}_wireStates(t){if(null===this._internals)return;const e=this._internals.states;for(const[s,i]of Object.entries(t))this.addEventListener(s,t=>{const s=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(i(t.detail))){try{n?e.add(r):e.delete(r)}catch{}s&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get url(){return this.getAttribute("url")||""}set url(t){g(this,"url",t)}get method(){return(this.getAttribute("method")||"POST").toUpperCase()}set method(t){g(this,"method",t)}get fieldName(){return this.getAttribute("field-name")||"file"}set fieldName(t){g(this,"field-name",t)}get multiple(){return this.hasAttribute("multiple")}set multiple(t){f(this,"multiple",t)}get maxSize(){const t=this.getAttribute("max-size");if(null===t)return 1/0;const e=parseInt(t,10);return Number.isFinite(e)&&e>=0?e:1/0}set maxSize(t){g(this,"max-size",t)}get accept(){return this.getAttribute("accept")||""}set accept(t){g(this,"accept",t)}get manual(){return this.hasAttribute("manual")}set manual(t){f(this,"manual",t)}get value(){return this._core.value}get loading(){return this._core.loading}get progress(){return this._core.progress}get error(){return this._core.error}get status(){return this._core.status}get errorInfo(){return this._core.errorInfo}get promise(){return this._core.promise}get trigger(){return this._trigger}set trigger(t){!!t&&(this._trigger=!0,this.upload().finally(()=>{this._trigger=!1,this.dispatchEvent(new CustomEvent("wcs-upload:trigger-changed",{detail:!1,bubbles:!0}))}))}get files(){return this._files}set files(t){const e=t??null;this._files=e,this.dispatchEvent(new CustomEvent("wcs-upload:files-changed",{detail:e,bubbles:!0})),!this.manual&&this.url&&e&&e.length>0&&this.upload()}_validate(t){const e=this.maxSize;if(e!==1/0)for(let s=0;s<t.length;s++)if(t[s].size>e)return{message:`File "${t[s].name}" exceeds maximum size of ${e} bytes.`};const s=this.accept;if(s){const e=s.split(",").map(t=>t.trim().toLowerCase());for(let i=0;i<t.length;i++){const r=t[i],n=r.type.toLowerCase(),a=r.name.toLowerCase();if(!e.some(t=>t.startsWith(".")?a.endsWith(t):t.endsWith("/*")?""!==n&&n.startsWith(t.slice(0,-1)):""!==n&&n===t))return{message:`File "${r.name}" does not match accepted types: ${s}`}}}return null}abort(){this._core.abort()}async upload(){const t=this._files;if(!t||0===t.length||!this.url)return null;const e=this._validate(t);if(e)return this._core.setError(e),null;const s=await this._core.upload(this.url,t,{method:this.method,fieldName:this.fieldName});return this._files===t&&(this._files=null,this.dispatchEvent(new CustomEvent("wcs-upload:files-changed",{detail:null,bubbles:!0}))),s}attributeChangedCallback(t,e,s){}connectedCallback(){!function(t){const e=t.constructor?.wcBindable,s=e?.inputs;if(void 0!==s)for(const e of s){const s=e.name;if(!Object.prototype.hasOwnProperty.call(t,s))continue;if(!d(t,s))continue;const i=t,r=i[s];delete i[s],i[s]=r}}(this),this.style.display="none",h||(h=!0,document.addEventListener("click",u)),this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var v;!function(e=customElements){e.get(t.tagNames.upload)||e.define(t.tagNames.upload,b)}(v);
1
+ const t={triggerAttribute:"data-uploadtarget",tagNames:{upload:"wcs-upload"}};class e{laneKey;policy;_ownerGeneration=0;_latestEpoch=0;_nextOperationId=1;_activeOperationId=void 0;_activeOperationIds=new Set;_queue=[];_inFlightCount=0;_terminal=new Map;_claimedOutcome=new Map;_controllers=new Map;_attempts=new Map;_withSignal;_trace;constructor(t,e,i={}){this.laneKey=t,this.policy=e,this._withSignal=i.withSignal??!1,this._trace=i.trace}get ownerGeneration(){return this._ownerGeneration}get inFlightCount(){return this._inFlightCount}get latestEpoch(){return this._latestEpoch}get activeOperationId(){return this._activeOperationId}begin(){let t;switch(this.policy){case"latest":t=++this._latestEpoch,void 0!==this._activeOperationId&&this._abortController(this._activeOperationId);break;case"exhaust":if(void 0!==this._activeOperationId)return null}const e=this._nextOperationId++,i={operationId:e,ownerGeneration:this._ownerGeneration,laneKey:this.laneKey,policy:this.policy,supersedeEpoch:t};switch(this.policy){case"latest":case"exhaust":this._activeOperationId=e;break;case"queue":this._queue.push(i),void 0===this._activeOperationId&&(this._activeOperationId=e);break;case"overlap":this._activeOperationIds.add(e)}this._inFlightCount+=1,this._attempts.set(e,1);const s=this._makeAttempt(e,1);return void 0!==this._trace&&this._trace({type:"io:operation-started",operationId:e,laneKey:this.laneKey,policy:this.policy}),{ticket:i,attempt:s}}retry(t){if(t.ownerGeneration!==this._ownerGeneration)return null;if(this._terminal.has(t.operationId))return null;const e=this._attempts.get(t.operationId);if(void 0===e)return null;const i=e+1;this._attempts.set(t.operationId,i),this._releaseController(t.operationId);const s=this._makeAttempt(t.operationId,i);return void 0!==this._trace&&this._trace({type:"io:operation-retried",operationId:t.operationId,laneKey:this.laneKey,attempt:i}),s}canCommit(t){if(t.ownerGeneration!==this._ownerGeneration)return!1;const e=this._terminal.get(t.operationId);return(void 0===e||"committing"===e)&&this._isEligible(t)}claimTerminal(t,e){return t.ownerGeneration===this._ownerGeneration&&(!this._terminal.has(t.operationId)&&(!!this._isEligible(t)&&(this._terminal.set(t.operationId,"committing"),this._claimedOutcome.set(t.operationId,e),!0)))}claimedOutcome(t){return this._claimedOutcome.get(t.operationId)}finalize(t){const e=t.operationId,i=this._terminal.get(e);if(void 0!==i&&"committing"!==i)return;let s;s="committing"===i?this._claimedOutcome.get(e)??"stale":"stale",this._terminal.set(e,s),this._claimedOutcome.delete(e),this._releaseController(e),this._attempts.delete(e),this._inFlightCount>0&&(this._inFlightCount-=1),this._advanceBookkeeping(e),void 0!==this._trace&&("stale"===s?this._trace({type:"io:stale-dropped",operationId:e,laneKey:this.laneKey}):this._trace({type:"io:operation-settled",operationId:e,laneKey:this.laneKey,outcome:s}))}signalOf(t){return this._controllers.get(t.operationId)?.signal}abort(t){this._abortController(t.operationId)}abortActive(){void 0!==this._activeOperationId&&this._abortController(this._activeOperationId);for(const t of this._activeOperationIds)this._abortController(t)}disposeOwner(){this._ownerGeneration+=1;for(const t of Array.from(this._controllers.keys()))this._abortController(t),this._releaseController(t);for(const t of Array.from(this._attempts.keys()))this._terminal.has(t)||this._terminal.set(t,"stale"),this._claimedOutcome.delete(t),this._attempts.delete(t),void 0!==this._trace&&this._trace({type:"io:stale-dropped",operationId:t,laneKey:this.laneKey});this._activeOperationId=void 0,this._activeOperationIds.clear(),this._queue.length=0,this._inFlightCount=0}_makeAttempt(t,e){let i;if(this._withSignal&&"function"==typeof AbortController){const e=new AbortController;this._controllers.set(t,e),i=e.signal}return{operationId:t,attempt:e,signal:i}}_isEligible(t){switch(this.policy){case"latest":return t.supersedeEpoch===this._latestEpoch;case"queue":case"exhaust":return this._activeOperationId===t.operationId;case"overlap":return this._activeOperationIds.has(t.operationId)}}_advanceBookkeeping(t){switch(this.policy){case"latest":case"exhaust":this._activeOperationId===t&&(this._activeOperationId=void 0);break;case"queue":{const e=this._queue.filter(e=>e.operationId!==t);this._queue.length=0,this._queue.push(...e),this._activeOperationId=this._queue.length>0?this._queue[0].operationId:void 0;break}case"overlap":this._activeOperationIds.delete(t)}}_abortController(t){const e=this._controllers.get(t);void 0===e||e.signal.aborted||e.abort()}_releaseController(t){this._controllers.delete(t)}}function i(t,e){const i=new Map,s=e=>{const i=t.get(e);return void 0===i?"unknown":i.probe()?"available":"missing"};let r=!0;for(const t of e.required){const e=s(t);i.set(t,e),"available"!==e&&(r=!1)}let n=!0;for(const t of e.optional??[]){const e=s(t);i.set(t,e),"available"!==e&&(n=!1)}const a=r?n?"ready":"degraded":"idle",o=[...e.required,...e.optional??[]],l=o.some(e=>!0===t.get(e)?.requiresSecureContext),c=o.some(e=>!0===t.get(e)?.requiresUserActivation),h=l?!0===globalThis.isSecureContext?"satisfied":"required":"not-applicable",u=c?"required":"not-applicable";return{availability:i,permission:e.permission??"not-applicable",readiness:a,activity:e.activity??"inactive",preconditions:{secureContext:h,userActivation:u},epoch:e.epoch??0,lastError:e.lastError}}function s(t,e){return e.every(e=>"available"===t.availability.get(e))}const r="capability-missing",n="invalid-argument",a="network",o="http-error",l=new Map([["web.xhr",{probe:()=>"function"==typeof globalThis.XMLHttpRequest,compatKey:"api.XMLHttpRequest"}]]);class c extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"value",event:"wcs-upload:response",semantics:"state",getter:t=>t.detail.value},{name:"loading",event:"wcs-upload:loading-changed",semantics:"state"},{name:"progress",event:"wcs-upload:progress",semantics:"state"},{name:"error",event:"wcs-upload:error",semantics:"state"},{name:"status",event:"wcs-upload:response",semantics:"state",getter:t=>t.detail.status},{name:"errorInfo",event:"wcs-upload:error-info-changed",semantics:"state"}],inputs:[{name:"url"},{name:"method"},{name:"fieldName"}],commands:[{name:"upload",async:!0},{name:"abort"}]};static REQUIRED_CAPABILITIES=["web.xhr"];_target;_value=null;_loading=!1;_progress=0;_error=null;_status=0;_errorInfo=null;_xhr=null;_promise=Promise.resolve(null);_lane=new e("upload","latest",{withSignal:!1});_ready=Promise.resolve();constructor(t){super(),this._target=t??this}get ready(){return this._ready}observe(){return this._ready}dispose(){this._lane.disposeOwner(),this.abort(),this._loading&&this._setLoading(!1)}get value(){return this._value}get loading(){return this._loading}get progress(){return this._progress}get error(){return this._error}get status(){return this._status}get promise(){return this._promise}get errorInfo(){return this._errorInfo}get supported(){return s(this.platformAssessment,c.REQUIRED_CAPABILITIES)}get platformAssessment(){return i(l,{required:c.REQUIRED_CAPABILITIES,activity:this._loading?"active":"inactive",lastError:this._errorInfo??void 0})}_commitStep(t,e){this._lane.canCommit(t)&&e()}_setLoading(t){this._loading=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:loading-changed",{detail:t,bubbles:!0}))}_setProgress(t){this._progress=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:progress",{detail:t,bubbles:!0}))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:error",{detail:t,bubbles:!0})))}setError(t){this._setError(t)}_setResponse(t,e){this._value=t,this._status=e,this._target.dispatchEvent(new CustomEvent("wcs-upload:response",{detail:{value:t,status:e},bubbles:!0}))}_setErrorInfo(t,e,i,s,r){this._commitErrorInfo({code:t,phase:e,recoverable:i,message:s,...void 0===r?{}:{capabilityId:r}})}_commitErrorInfo(t){this._errorInfo!==t&&(this._errorInfo=t,this._target.dispatchEvent(new CustomEvent("wcs-upload:error-info-changed",{detail:t,bubbles:!0})))}abort(){this._xhr&&(this._xhr.abort(),this._xhr=null)}async upload(t,e,i={}){if(!t)return this._setErrorInfo(n,"start",!1,"url is required."),this._setError({message:"url is required."}),null;if(!e||0===e.length)return this._setErrorInfo(n,"start",!1,"files are required."),this._setError({message:"files are required."}),null;const s=this._doUpload(t,e,i);return this._promise=s,s}_doUpload(t,e,i){const n=this.platformAssessment;if(!s(n,c.REQUIRED_CAPABILITIES)){const t=c.REQUIRED_CAPABILITIES.find(t=>"available"!==n.availability.get(t)),e=`Required capability "${t}" is unavailable.`;return this._setErrorInfo(r,"start",!1,e,t),this._setError({message:e}),Promise.resolve(null)}this.abort();const l=this._lane.begin(),{ticket:h}=l;this._commitStep(h,()=>this._setLoading(!0)),this._commitStep(h,()=>{this._setProgress(0),this._commitErrorInfo(null),this._setError(null)});const{method:u="POST",headers:d={},fieldName:p="file"}=i,_=new FormData;for(let t=0;t<e.length;t++)_.append(p,e[t]);return new Promise(e=>{const i=new XMLHttpRequest;this._xhr=i,i.upload.addEventListener("progress",t=>{this._commitStep(h,()=>{t.lengthComputable&&this._setProgress(Math.round(t.loaded/t.total*100))})}),i.addEventListener("load",()=>{if(this._xhr=null,i.status>=200&&i.status<300){if(!this._lane.claimTerminal(h,"success"))return e(null),void this._lane.finalize(h);let t=i.responseText;if((i.getResponseHeader("Content-Type")||"").includes("application/json"))try{t=JSON.parse(i.responseText)}catch{}this._commitStep(h,()=>this._setProgress(100)),this._commitStep(h,()=>this._setResponse(t,i.status)),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(t)}else{if(!this._lane.claimTerminal(h,"error"))return e(null),void this._lane.finalize(h);const t={status:i.status,statusText:i.statusText,body:i.responseText};this._commitStep(h,()=>{this._status=i.status,this._setErrorInfo(o,"execute",!0,`HTTP ${i.status} ${i.statusText}`),this._setError(t)}),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}}),i.addEventListener("error",()=>{if(this._xhr=null,!this._lane.claimTerminal(h,"error"))return e(null),void this._lane.finalize(h);const t="Network error";this._commitStep(h,()=>{this._setErrorInfo(a,"execute",!0,t),this._setError({message:t})}),this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}),i.addEventListener("abort",()=>{this._xhr=null,this._lane.claimTerminal(h,"aborted")&&this._commitStep(h,()=>this._setLoading(!1)),this._lane.finalize(h),e(null)}),i.open(u,t);for(const[t,e]of Object.entries(d))i.setRequestHeader(t,e);i.send(_)})}}let h=!1;function u(e){const i=e.target;if(!(i instanceof Element))return;const s=i.closest(`[${t.triggerAttribute}]`);if(!s)return;const r=s.getAttribute(t.triggerAttribute);if(!r)return;const n=customElements.get(t.tagNames.upload),a=document.getElementById(r);if(!(n&&a instanceof n))return;const o=a;o.files&&o.files.length>0&&o.url&&e.preventDefault(),o.upload()}function d(t,e){let i=Object.getPrototypeOf(t);for(;null!==i;){const t=Object.getOwnPropertyDescriptor(i,e);if(void 0!==t)return"function"==typeof t.get||"function"==typeof t.set;i=Object.getPrototypeOf(i)}return!1}const p=new WeakSet;function _(t,e,i){if(p.has(t))return;p.add(t);const s=t.localName??"element";console.warn(`[wcs/input-as-attribute] <${s}> has the attribute ${e}="${i}": an object was written as an attribute, so the element did not receive it. A framework does that when the property is set before the element's definition loads (React 19), or when told to (Vue's .attr). Load the definition before rendering, or assign the property through a ref (Vue: .prop). (Said once per element.)`,t)}const m=new WeakMap;function g(t,e){let i=m.get(t);return void 0===i&&(i=new Map,m.set(t,i)),i.has(e)||i.set(e,t.getAttribute(e)),i.get(e)}function f(t,e,i){null===i?t.removeAttribute(e):t.setAttribute(e,i)}function b(t,e,i){const s=g(t,e);f(t,e,void 0===i?s:null===i?null:String(i))}function v(t,e,i){const s=g(t,e);f(t,e,void 0===i?s:i?"":null)}class w extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...c.wcBindable,properties:[...c.wcBindable.properties,{name:"trigger",event:"wcs-upload:trigger-changed",semantics:"state"},{name:"files",event:"wcs-upload:files-changed",semantics:"state"}],inputs:[{name:"url"},{name:"method"},{name:"fieldName"},{name:"multiple"},{name:"maxSize"},{name:"accept"},{name:"manual"},{name:"files"},{name:"trigger"}]};static get observedAttributes(){return["url"]}_core;_files=null;_trigger=!1;_told=null;_waitingForUrl=!1;_connectedCallbackPromise=Promise.resolve();_internals=null;constructor(){super(),this._core=new c(this),this._internals=this._initInternals(),this._wireStates({"wcs-upload:loading-changed":t=>({loading:!0===t}),"wcs-upload:error":t=>({error:null!=t})})}get debugStates(){return this._internals?[...this._internals.states]:[]}_initInternals(){try{if("function"!=typeof this.attachInternals)return null;const t=this.attachInternals();return t.states.add("wcs-probe"),t.states.delete("wcs-probe"),t}catch{return null}}_wireStates(t){if(null===this._internals)return;const e=this._internals.states;for(const[i,s]of Object.entries(t))this.addEventListener(i,t=>{const i=this.hasAttribute("debug-states");for(const[r,n]of Object.entries(s(t.detail))){try{n?e.add(r):e.delete(r)}catch{}i&&this.toggleAttribute(`data-wcs-state-${r}`,n)}})}get connectedCallbackPromise(){return this._connectedCallbackPromise}get url(){return this.getAttribute("url")||""}set url(t){b(this,"url",t)}get method(){return(this.getAttribute("method")||"POST").toUpperCase()}set method(t){b(this,"method",t)}get fieldName(){return this.getAttribute("field-name")||"file"}set fieldName(t){b(this,"field-name",t)}get multiple(){return this.hasAttribute("multiple")}set multiple(t){v(this,"multiple",t)}get maxSize(){const t=this.getAttribute("max-size");if(null===t)return 1/0;const e=parseInt(t,10);return Number.isFinite(e)&&e>=0?e:1/0}set maxSize(t){b(this,"max-size",t)}get accept(){return this.getAttribute("accept")||""}set accept(t){b(this,"accept",t)}get manual(){return this.hasAttribute("manual")}set manual(t){v(this,"manual",t)}get value(){return this._core.value}get loading(){return this._core.loading}get progress(){return this._core.progress}get error(){return this._core.error}get status(){return this._core.status}get errorInfo(){return this._core.errorInfo}get promise(){return this._core.promise}get trigger(){return this._trigger}set trigger(t){!!t&&(this._trigger=!0,this.upload().finally(()=>{this._trigger=!1,this.dispatchEvent(new CustomEvent("wcs-upload:trigger-changed",{detail:!1,bubbles:!0}))}))}get files(){return this._files}set files(t){const e=t??null;this._files=e,this.dispatchEvent(new CustomEvent("wcs-upload:files-changed",{detail:e,bubbles:!0})),this._waitingForUrl=!this.manual&&!!e&&e.length>0&&!this.url,!this.manual&&e&&e.length>0&&this.url&&this.upload()}_validate(t){const e=this.maxSize;if(e!==1/0)for(let i=0;i<t.length;i++)if(t[i].size>e)return{message:`File "${t[i].name}" exceeds maximum size of ${e} bytes.`};const i=this.accept;if(i){const e=i.split(",").map(t=>t.trim().toLowerCase());for(let s=0;s<t.length;s++){const r=t[s],n=r.type.toLowerCase(),a=r.name.toLowerCase();if(!e.some(t=>t.startsWith(".")?a.endsWith(t):t.endsWith("/*")?""!==n&&n.startsWith(t.slice(0,-1)):""!==n&&n===t))return{message:`File "${r.name}" does not match accepted types: ${i}`}}}return null}abort(){this._core.abort()}async upload(){const t=this._files;if(!t||0===t.length||!this.url)return this._warnNotReady(this.url?"there are no files":"there is no url"),null;const e=this._validate(t);if(e)return this._core.setError(e),null;const i=await this._core.upload(this.url,t,{method:this.method,fieldName:this.fieldName});return this._files===t&&(this._files=null,this.dispatchEvent(new CustomEvent("wcs-upload:files-changed",{detail:null,bubbles:!0}))),i}_warnNotReady(t){this._told?.has(t)||((this._told??=new Set).add(t),console.warn(`[@wcstack/upload] [wcs/command-not-ready] The upload was refused: ${t}, so nothing is sent. Write url and files before trigger (data-wcs bindings and a framework's props are applied in the order written), and assign files again for each retry: an upload uses them up. (Said once per element and reason.)`,this))}attributeChangedCallback(t,e,i){this._waitingForUrl&&i&&!this.manual&&(this._waitingForUrl=!1,this.upload())}connectedCallback(){!function(t){const e=t.constructor?.wcBindable,i=e?.inputs;if(void 0===i)return;const s=t.getAttribute;for(const e of i){const i=e.name,r="function"==typeof s?s.call(t,i.toLowerCase()):null;if(null!==r&&r.startsWith("[object ")&&_(t,i,r),!Object.prototype.hasOwnProperty.call(t,i))continue;if(!d(t,i))continue;const n=t,a=n[i];delete n[i],n[i]=a}}(this),this.style.display="none",h||(h=!0,document.addEventListener("click",u)),this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}var I;!function(e=customElements){e.get(t.tagNames.upload)||e.define(t.tagNames.upload,w)}(I);
2
2
  //# sourceMappingURL=auto.min.js.map