@ram_28/kf-ai-sdk 2.0.20-beta.0 → 2.0.20-beta.2

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/dist/workflow.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var ne=Object.defineProperty;var ie=(o,t,e)=>t in o?ne(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var V=(o,t,e)=>ie(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./client-D5k4SYuw.cjs"),A=require("./FileField-eDeuzln8.cjs"),h=require("react"),k=require("./shared-nnmlRVs7.cjs"),oe=require("@tanstack/react-query"),z=require("./attachment-constants-B5jlqoKI.cjs"),G=require("./useTable-CeRklbdT.cjs"),ce=require("./metadata-DWXQPDav.cjs");class W{constructor(t){V(this,"bp_id");this.bp_id=t}async start(){const t=await fetch(`${y.getApiBaseUrl()}/api/app/process/${this.bp_id}/start`,{method:"POST",headers:y.getDefaultHeaders()});if(!t.ok)throw new Error(`Failed to start process: ${t.statusText}`);return(await t.json()).Data}async progress(t){const e=await fetch(`${y.getApiBaseUrl()}/api/app/process/${this.bp_id}/${t}/progress`,{method:"GET",headers:y.getDefaultHeaders()});if(!e.ok)throw new Error(`Failed to get process progress: ${e.statusText}`);return(await e.json()).Data}activity(t){const e=`/api/app/process/${this.bp_id}/${t}`;return{async inProgressList(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/list`,{method:"POST",headers:y.getDefaultHeaders(),body:a?JSON.stringify(a):void 0});if(!r.ok)throw new Error(`Failed to list in-progress activities: ${r.statusText}`);return r.json()},async completedList(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/completed/list`,{method:"POST",headers:y.getDefaultHeaders(),body:a?JSON.stringify(a):void 0});if(!r.ok)throw new Error(`Failed to list completed activities: ${r.statusText}`);return r.json()},async inProgressCount(a){var w,s;const r={Type:"Metric",GroupBy:[],Metric:[{Field:"_id",Type:"Count"}],...(a==null?void 0:a.Filter)&&{Filter:a.Filter}},i=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get in-progress count: ${i.statusText}`);return{Count:((s=(w=(await i.json()).Data)==null?void 0:w[0])==null?void 0:s.count__id)??0}},async completedCount(a){var w,s;const r={Type:"Metric",GroupBy:[],Metric:[{Field:"_id",Type:"Count"}],...(a==null?void 0:a.Filter)&&{Filter:a.Filter}},i=await fetch(`${y.getApiBaseUrl()}${e}/completed/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get completed count: ${i.statusText}`);return{Count:((s=(w=(await i.json()).Data)==null?void 0:w[0])==null?void 0:s.count__id)??0}},async inProgressMetric(a){const r={Type:"Metric",...a},i=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get in-progress metrics: ${i.statusText}`);return i.json()},async completedMetric(a){const r={Type:"Metric",...a},i=await fetch(`${y.getApiBaseUrl()}${e}/completed/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get completed metrics: ${i.statusText}`);return i.json()},async read(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/read`,{method:"GET",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to read activity: ${r.statusText}`);return(await r.json()).Data},async update(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/update`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to update activity: ${i.statusText}`);return i.json()},async draftStart(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/draft`,{method:"PATCH",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to start draft: ${i.statusText}`);return i.json()},async draftEnd(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/draft`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to end draft: ${i.statusText}`);return i.json()},async complete(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/done`,{method:"POST",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to complete activity: ${r.statusText}`);return r.json()},async progress(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/progress`,{method:"GET",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to get activity progress: ${r.statusText}`);return r.json()}}}}class X{constructor(t,e,a,r){V(this,"_id");V(this,"_data");V(this,"_ops");V(this,"_fields");V(this,"_accessorCache",new Map);this._ops=t,this._id=e,this._data={...a},this._fields=r}toJSON(){return{...this._data}}validate(){const t=[];for(const[e,a]of Object.entries(this._fields)){const r=this._data[e],i=a.validate(r);i.valid||t.push(...i.errors)}return{valid:t.length===0,errors:t}}async update(t){return this._ops.update(this._id,t)}async save(t){return this._ops.draftEnd(this._id,t)}async complete(){return this._ops.complete(this._id)}async progress(){return this._ops.progress(this._id)}_getAccessor(t){if(this._accessorCache.has(t))return this._accessorCache.get(t);const e=this._fields[t],a=(e==null?void 0:e.meta)??{_id:t,Name:t,Type:"String"},r=(e==null?void 0:e.readOnly)??!1,i=()=>e?e.validate(this._data[t]):{valid:!0,errors:[]};let n;return r?n={label:(e==null?void 0:e.label)??t,required:(e==null?void 0:e.required)??!1,readOnly:!0,defaultValue:e==null?void 0:e.defaultValue,meta:a,get:()=>this._data[t],validate:i}:n={label:(e==null?void 0:e.label)??t,required:(e==null?void 0:e.required)??!1,readOnly:!1,defaultValue:e==null?void 0:e.defaultValue,meta:a,get:()=>this._data[t],set:f=>{this._data[t]=f},validate:i},this._accessorCache.set(t,n),n}}function le(o,t,e,a){const r=new X(o,t,e,a);return new Proxy(r,{get(i,n,f){return n==="validate"||n==="toJSON"||n==="update"||n==="save"||n==="complete"||n==="progress"||n==="_ops"||n==="_data"||n==="_fields"||n==="_accessorCache"||n==="_getAccessor"||typeof n=="symbol"?Reflect.get(i,n,f):n==="_id"?i._id:i._getAccessor(n)},has(i,n){return typeof n=="symbol"?!1:n==="validate"||n==="toJSON"||n==="update"||n==="save"||n==="complete"||n==="progress"||n==="_id"?!0:n in i._fields},ownKeys(i){return[...Object.keys(i._fields),"_id","validate","toJSON","update","save","complete","progress"]},getOwnPropertyDescriptor(i,n){if(typeof n!="symbol")return{configurable:!0,enumerable:n!=="validate"&&n!=="toJSON"&&n!=="update"&&n!=="save"&&n!=="complete"&&n!=="progress"}}})}class ue{constructor(){V(this,"_fieldsCache",null)}_ops(){return new W(this.meta.businessProcessId).activity(this.meta.activityId)}_discoverFields(){if(this._fieldsCache)return this._fieldsCache;const t={};for(const e of Object.keys(this)){const a=this[e];a instanceof A.BaseField&&(t[e]=a)}return this._fieldsCache=t,t}async getInProgressList(t){return this._ops().inProgressList(t)}async getCompletedList(t){return this._ops().completedList(t)}async inProgressCount(t){return(await this._ops().inProgressCount(t)).Count}async completedCount(t){return(await this._ops().completedCount(t)).Count}async inProgressMetric(t){return this._ops().inProgressMetric(t)}async completedMetric(t){return this._ops().completedMetric(t)}async getInstance(t){const e=this._ops(),a=await e.read(t),r=this._discoverFields();return le(e,t,a,r)}_getFields(){return this._discoverFields()}_getOps(){return this._ops()}}function K(o,t){var r;const e={_id:o,...t},a=t.Type;if(a==="String"&&((r=t.Constraint)!=null&&r.Enum))return new A.SelectField(e);switch(a){case"String":return new A.StringField(e);case"Number":return new A.NumberField(e);case"Boolean":return new A.BooleanField(e);case"Date":return new A.DateField(e);case"DateTime":return new A.DateTimeField(e);case"Text":return new A.TextField(e);case"Reference":return new A.ReferenceField(e);case"User":return new A.UserField(e);case"File":return new A.FileField(e);default:return new A.StringField(e)}}function Y(o){const t={};for(const[e,a]of Object.entries(o))t[e]=K(e,a);return t}function de(o,t,e){var i;const a=t==null?void 0:t.BDOBlob;if(!(a!=null&&a.Activity))return null;const r=o==null?void 0:o.Id;for(const n of a.Activity)if(n.Id!==r&&(i=n.Input)!=null&&i[e])return n.Input[e];return null}function ye(o){return async(t,e,a)=>{const r={},i="_getFields"in o?o._getFields():o,n=(a==null?void 0:a.names)??Object.keys(i);for(const f of n){const w=i[f];if(!w||w.readOnly)continue;let s=t[f];typeof s=="string"&&w.meta.Type==="Number"&&(s=s===""?void 0:Number(s));const l=w.validate(s);if(!l.valid&&l.errors.length>0){r[f]={type:"validate",message:l.errors[0]||`${f} is invalid`};continue}const v=k.validateConstraints(w,s);!v.valid&&v.errors.length>0&&(r[f]={type:"constraint",message:v.errors[0]})}return Object.keys(r).length===0?{values:t,errors:{}}:{values:{},errors:r}}}function fe(o,t,e){const a=o._getFields(),r=new Map,i=`/api/app/process/${o.meta.businessProcessId}/${o.meta.activityId}`,n=y.createResourceClient(i);function f(){if(!e)throw new Error("Cannot perform attachment operation: no activity instance ID");return e}return new Proxy({},{get(w,s){if(typeof s=="symbol")return;if(s==="_id")return e;if(s==="toJSON")return()=>t.getValues();if(s==="validate")return()=>t.trigger();if(r.has(s))return r.get(s);const l=a[s],v=(l==null?void 0:l.meta)??{_id:s,Name:s,Type:"String"},E=(l==null?void 0:l.readOnly)??!1,I=()=>l?l.validate(t.getValues(s)):{valid:!0,errors:[]},q=$=>{const d=t.getValues(s);return d??$};if(!E){const $=()=>{const c=t.getValues(s);return v.Type==="File"?c??[]:c},d={label:(l==null?void 0:l.label)??s,required:(l==null?void 0:l.required)??!1,readOnly:!1,defaultValue:l==null?void 0:l.defaultValue,meta:v,get:$,getOrDefault:q,set:c=>{t.setValue(s,c,{shouldDirty:!0,shouldTouch:!0,shouldValidate:!1})},validate:I};return(v.Type==="Image"||v.Type==="File")&&(v.Type==="Image"?(d.upload=async c=>{z.validateFileExtension(c.name,"Image");const m=f(),[g]=await n.getUploadUrl(m,s,[{FileName:c.name,Size:c.size,FileExtension:z.extractFileExtension(c.name)}]);await fetch(g.UploadUrl.URL,{method:"PUT",headers:{"Content-Type":g.ContentType},body:c});const F={_id:g._id,_name:g._name,FileName:g.FileName,FileExtension:g.FileExtension,Size:g.Size,ContentType:g.ContentType};return t.setValue(s,F,{shouldDirty:!0}),F},d.deleteAttachment=async()=>{const c=t.getValues(s),m=f();if(!(c!=null&&c._id))throw new Error(`${s} has no image to delete`);await n.deleteAttachment(m,s,c._id),t.setValue(s,null,{shouldDirty:!0})},d.getDownloadUrl=async c=>{const m=t.getValues(s),g=f();if(!(m!=null&&m._id))throw new Error(`${s} has no image`);return n.getDownloadUrl(g,s,m._id,c)}):(d.upload=async c=>{for(const O of c)z.validateFileExtension(O.name,"File");const m=f(),g=c.map(O=>({FileName:O.name,Size:O.size,FileExtension:z.extractFileExtension(O.name)})),F=await n.getUploadUrl(m,s,g),R=await Promise.all(c.map(async(O,P)=>(await fetch(F[P].UploadUrl.URL,{method:"PUT",headers:{"Content-Type":F[P].ContentType},body:O}),{_id:F[P]._id,_name:F[P]._name,FileName:F[P].FileName,FileExtension:F[P].FileExtension,Size:F[P].Size,ContentType:F[P].ContentType}))),u=t.getValues(s)??[];return t.setValue(s,[...u,...R],{shouldDirty:!0}),R},d.deleteAttachment=async c=>{const m=t.getValues(s)??[],g=f();await n.deleteAttachment(g,s,c),t.setValue(s,m.filter(F=>F._id!==c),{shouldDirty:!0})},d.getDownloadUrl=async(c,m)=>{const g=f();return n.getDownloadUrl(g,s,c,m)},d.getDownloadUrls=async c=>{const m=f();return n.getDownloadUrls(m,s,c)})),r.set(s,d),d}const B=()=>{const $=t.getValues(s);return v.Type==="File"?$??[]:$},D={label:(l==null?void 0:l.label)??s,required:(l==null?void 0:l.required)??!1,readOnly:!0,defaultValue:l==null?void 0:l.defaultValue,meta:v,get:B,getOrDefault:q,validate:I};return(v.Type==="Image"||v.Type==="File")&&(v.Type==="Image"?D.getDownloadUrl=async $=>{const d=t.getValues(s),c=f();if(!(d!=null&&d._id))throw new Error(`${s} has no image to download`);return n.getDownloadUrl(c,s,d._id,$)}:(D.getDownloadUrl=async($,d)=>{const c=f();return n.getDownloadUrl(c,s,$,d)},D.getDownloadUrls=async $=>{const d=f();return n.getDownloadUrls(d,s,$)})),r.set(s,D),D},has(w,s){return typeof s=="symbol"?!1:s==="_id"||s==="toJSON"||s==="validate"?!0:s in a},ownKeys(w){return[...Object.keys(a),"_id","toJSON","validate"]},getOwnPropertyDescriptor(w,s){if(typeof s!="symbol")return{configurable:!0,enumerable:s!=="toJSON"&&s!=="validate"}}})}function ge(o,t){const{activity_instance_id:e,defaultValues:a={},mode:r="onBlur",enabled:i=!0}=t,[n,f]=h.useState(!1),[w,s]=h.useState(null),[l,v]=h.useState(!1),[E,I]=h.useState({}),q=h.useRef(!1),B=h.useRef(null),{data:D,isLoading:$}=oe.useQuery({queryKey:["bp-metadata",o.meta.businessProcessId],queryFn:()=>ce.getBdoSchema(o.meta.businessProcessId),staleTime:30*60*1e3}),d=h.useMemo(()=>{const _=D==null?void 0:D.BDOBlob;return _!=null&&_.Activity?_.Activity.find(b=>b.Id===o.meta.activityId)??null:null},[D,o.meta.activityId]),c=$,m=h.useMemo(()=>d!=null&&d.Input?Y(d.Input):o._getFields(),[d,o]),g=h.useMemo(()=>({...E,...m}),[E,m]),F=h.useMemo(()=>Object.keys(g).filter(_=>g[_].readOnly),[g]),R=h.useMemo(()=>ye(m),[m]),u=k.useForm({mode:r,defaultValues:a,resolver:R}),O=h.useMemo(()=>o._getOps(),[o]),P=h.useMemo(()=>fe(o,u,e),[o,u,e]);h.useEffect(()=>{if(!i||c)return;let _=!0;return(async()=>{f(!0),s(null);try{const T=await O.read(e);if(!_)return;if(T&&typeof T=="object"){const U=k.coerceRecordForForm(m,T),p={...a,...U};(B.current===null||T!==B.current)&&(u.reset(p),B.current=T);const M=new Set(["_id","BPInstanceId","Status","AssignedTo","CompletedAt","_created_at","_modified_at","_created_by","_modified_by","_v","_m_v"]),x={};for(const C of Object.keys(T))if(!m[C]&&!M.has(C)){const S=de(d,D,C);S&&(x[C]=K(C,{...S,ReadOnly:!0}))}Object.keys(x).length>0&&I(x)}}catch(T){if(!_)return;console.error("Failed to read activity data:",T),s(G.toError(T))}finally{_&&f(!1)}})(),()=>{_=!1}},[i,c,O,e]);const Q=h.useCallback((_,b)=>O.update(e,{[_]:b}),[O,e]),J=h.useMemo(()=>k.createSyncField({apiFn:Q,allFields:g,readonlyFieldNames:F,rhf:u,isComputingRef:q}),[Q,g,F,u]),L=r==="onChange"||r==="all",H=r==="onBlur"||r==="onTouched"||r==="all",Z=h.useMemo(()=>k.createEnhancedRegister({rhf:u,allFields:g,syncField:J,syncOnBlur:H,syncOnChange:L}),[u,g,J,H,L]),ee=h.useMemo(()=>k.createEnhancedControl({control:u.control,syncField:J,syncOnBlur:H,syncOnChange:L}),[u.control,J,H,L]),te=h.useCallback((_,b)=>u.handleSubmit(async(T,U)=>{v(!0);try{const p={},M=new Set(F),x=u.formState.dirtyFields,C=u.getValues();for(const S of Object.keys(C)){if(M.has(S)||!x[S])continue;const N=C[S]!==void 0?C[S]:T[S],j=g[S];p[S]=j?k.coerceFieldValue(j,N):N}Object.keys(p).length>0&&await O.update(e,p),await(_==null?void 0:_(T,U))}catch(p){b==null||b(G.toError(p),U)}finally{v(!1)}},(T,U)=>{b==null||b(T,U)}),[u,O,F,g,e]),se=h.useCallback((_,b)=>u.handleSubmit(async(T,U)=>{v(!0);try{const p={},M=new Set(F),x=u.formState.dirtyFields,C=u.getValues();for(const S of Object.keys(C)){if(M.has(S)||!x[S])continue;const N=C[S]!==void 0?C[S]:T[S],j=g[S];p[S]=j?k.coerceFieldValue(j,N):N}Object.keys(p).length>0&&await O.update(e,p),await O.complete(e),await(_==null?void 0:_(T,U))}catch(p){b==null||b(G.toError(p),U)}finally{v(!1)}},(T,U)=>{b==null||b(T,U)}),[u,O,F,g,e]),ae=h.useCallback(()=>{u.clearErrors()},[u]),re=!!w;return{item:P,activity:o,register:Z,handleSubmit:te,handleComplete:se,watch:u.watch,setValue:u.setValue,getValues:u.getValues,reset:u.reset,trigger:u.trigger,control:ee,errors:u.formState.errors,isValid:u.formState.isValid,isDirty:u.formState.isDirty,isSubmitting:u.formState.isSubmitting||l,isSubmitSuccessful:u.formState.isSubmitSuccessful,isLoading:n||c,isMetadataLoading:c,loadError:w,hasError:re,bpMetadata:D??null,clearErrors:ae}}function he(o){const{activity:t,status:e,...a}=o,{businessProcessId:r,activityId:i}=t.meta,n=h.useMemo(()=>t._getOps(),[t]),f=h.useMemo(()=>e==="inprogress"?s=>n.inProgressList(s):s=>n.completedList(s),[n,e]),w=h.useMemo(()=>e==="inprogress"?s=>n.inProgressCount(s):s=>n.completedCount(s),[n,e]);return G.useTable({queryKey:["activity-table",r,i,e],listFn:f,countFn:w,...a})}const me={InProgress:"inprogress",Completed:"completed"};exports.Activity=ue;exports.ActivityInstance=X;exports.ActivityTableStatus=me;exports.Workflow=W;exports.buildFieldsFromInput=Y;exports.createFieldFromMeta=K;exports.useActivityForm=ge;exports.useActivityTable=he;
1
+ "use strict";var ne=Object.defineProperty;var ie=(o,t,e)=>t in o?ne(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var V=(o,t,e)=>ie(o,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./client-D5k4SYuw.cjs"),A=require("./FileField-DU4UWo_t.cjs"),h=require("react"),k=require("./shared-nnmlRVs7.cjs"),oe=require("@tanstack/react-query"),z=require("./attachment-constants-B5jlqoKI.cjs"),G=require("./useTable-CeRklbdT.cjs"),ce=require("./metadata-DWXQPDav.cjs");class W{constructor(t){V(this,"bp_id");this.bp_id=t}async start(){const t=await fetch(`${y.getApiBaseUrl()}/api/app/process/${this.bp_id}/start`,{method:"POST",headers:y.getDefaultHeaders()});if(!t.ok)throw new Error(`Failed to start process: ${t.statusText}`);return(await t.json()).Data}async progress(t){const e=await fetch(`${y.getApiBaseUrl()}/api/app/process/${this.bp_id}/${t}/progress`,{method:"GET",headers:y.getDefaultHeaders()});if(!e.ok)throw new Error(`Failed to get process progress: ${e.statusText}`);return(await e.json()).Data}activity(t){const e=`/api/app/process/${this.bp_id}/${t}`;return{async inProgressList(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/list`,{method:"POST",headers:y.getDefaultHeaders(),body:a?JSON.stringify(a):void 0});if(!r.ok)throw new Error(`Failed to list in-progress activities: ${r.statusText}`);return r.json()},async completedList(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/completed/list`,{method:"POST",headers:y.getDefaultHeaders(),body:a?JSON.stringify(a):void 0});if(!r.ok)throw new Error(`Failed to list completed activities: ${r.statusText}`);return r.json()},async inProgressCount(a){var w,s;const r={Type:"Metric",GroupBy:[],Metric:[{Field:"_id",Type:"Count"}],...(a==null?void 0:a.Filter)&&{Filter:a.Filter}},i=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get in-progress count: ${i.statusText}`);return{Count:((s=(w=(await i.json()).Data)==null?void 0:w[0])==null?void 0:s.count__id)??0}},async completedCount(a){var w,s;const r={Type:"Metric",GroupBy:[],Metric:[{Field:"_id",Type:"Count"}],...(a==null?void 0:a.Filter)&&{Filter:a.Filter}},i=await fetch(`${y.getApiBaseUrl()}${e}/completed/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get completed count: ${i.statusText}`);return{Count:((s=(w=(await i.json()).Data)==null?void 0:w[0])==null?void 0:s.count__id)??0}},async inProgressMetric(a){const r={Type:"Metric",...a},i=await fetch(`${y.getApiBaseUrl()}${e}/inprogress/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get in-progress metrics: ${i.statusText}`);return i.json()},async completedMetric(a){const r={Type:"Metric",...a},i=await fetch(`${y.getApiBaseUrl()}${e}/completed/metric`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to get completed metrics: ${i.statusText}`);return i.json()},async read(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/read`,{method:"GET",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to read activity: ${r.statusText}`);return(await r.json()).Data},async update(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/update`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to update activity: ${i.statusText}`);return i.json()},async draftStart(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/draft`,{method:"PATCH",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to start draft: ${i.statusText}`);return i.json()},async draftEnd(a,r){const i=await fetch(`${y.getApiBaseUrl()}${e}/${a}/draft`,{method:"POST",headers:y.getDefaultHeaders(),body:JSON.stringify(r)});if(!i.ok)throw new Error(`Failed to end draft: ${i.statusText}`);return i.json()},async complete(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/done`,{method:"POST",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to complete activity: ${r.statusText}`);return r.json()},async progress(a){const r=await fetch(`${y.getApiBaseUrl()}${e}/${a}/progress`,{method:"GET",headers:y.getDefaultHeaders()});if(!r.ok)throw new Error(`Failed to get activity progress: ${r.statusText}`);return r.json()}}}}class X{constructor(t,e,a,r){V(this,"_id");V(this,"_data");V(this,"_ops");V(this,"_fields");V(this,"_accessorCache",new Map);this._ops=t,this._id=e,this._data={...a},this._fields=r}toJSON(){return{...this._data}}validate(){const t=[];for(const[e,a]of Object.entries(this._fields)){const r=this._data[e],i=a.validate(r);i.valid||t.push(...i.errors)}return{valid:t.length===0,errors:t}}async update(t){return this._ops.update(this._id,t)}async save(t){return this._ops.draftEnd(this._id,t)}async complete(){return this._ops.complete(this._id)}async progress(){return this._ops.progress(this._id)}_getAccessor(t){if(this._accessorCache.has(t))return this._accessorCache.get(t);const e=this._fields[t],a=(e==null?void 0:e.meta)??{_id:t,Name:t,Type:"String"},r=(e==null?void 0:e.readOnly)??!1,i=()=>e?e.validate(this._data[t]):{valid:!0,errors:[]};let n;return r?n={label:(e==null?void 0:e.label)??t,required:(e==null?void 0:e.required)??!1,readOnly:!0,defaultValue:e==null?void 0:e.defaultValue,meta:a,get:()=>this._data[t],validate:i}:n={label:(e==null?void 0:e.label)??t,required:(e==null?void 0:e.required)??!1,readOnly:!1,defaultValue:e==null?void 0:e.defaultValue,meta:a,get:()=>this._data[t],set:f=>{this._data[t]=f},validate:i},this._accessorCache.set(t,n),n}}function le(o,t,e,a){const r=new X(o,t,e,a);return new Proxy(r,{get(i,n,f){return n==="validate"||n==="toJSON"||n==="update"||n==="save"||n==="complete"||n==="progress"||n==="_ops"||n==="_data"||n==="_fields"||n==="_accessorCache"||n==="_getAccessor"||typeof n=="symbol"?Reflect.get(i,n,f):n==="_id"?i._id:i._getAccessor(n)},has(i,n){return typeof n=="symbol"?!1:n==="validate"||n==="toJSON"||n==="update"||n==="save"||n==="complete"||n==="progress"||n==="_id"?!0:n in i._fields},ownKeys(i){return[...Object.keys(i._fields),"_id","validate","toJSON","update","save","complete","progress"]},getOwnPropertyDescriptor(i,n){if(typeof n!="symbol")return{configurable:!0,enumerable:n!=="validate"&&n!=="toJSON"&&n!=="update"&&n!=="save"&&n!=="complete"&&n!=="progress"}}})}class ue{constructor(){V(this,"_fieldsCache",null)}_ops(){return new W(this.meta.businessProcessId).activity(this.meta.activityId)}_discoverFields(){if(this._fieldsCache)return this._fieldsCache;const t={};for(const e of Object.keys(this)){const a=this[e];a instanceof A.BaseField&&(t[e]=a)}return this._fieldsCache=t,t}async getInProgressList(t){return this._ops().inProgressList(t)}async getCompletedList(t){return this._ops().completedList(t)}async inProgressCount(t){return(await this._ops().inProgressCount(t)).Count}async completedCount(t){return(await this._ops().completedCount(t)).Count}async inProgressMetric(t){return this._ops().inProgressMetric(t)}async completedMetric(t){return this._ops().completedMetric(t)}async getInstance(t){const e=this._ops(),a=await e.read(t),r=this._discoverFields();return le(e,t,a,r)}_getFields(){return this._discoverFields()}_getOps(){return this._ops()}}function K(o,t){var r;const e={_id:o,...t},a=t.Type;if(a==="String"&&((r=t.Constraint)!=null&&r.Enum))return new A.SelectField(e);switch(a){case"String":return new A.StringField(e);case"Number":return new A.NumberField(e);case"Boolean":return new A.BooleanField(e);case"Date":return new A.DateField(e);case"DateTime":return new A.DateTimeField(e);case"Text":return new A.TextField(e);case"Reference":return new A.ReferenceField(e);case"User":return new A.UserField(e);case"File":return new A.FileField(e);default:return new A.StringField(e)}}function Y(o){const t={};for(const[e,a]of Object.entries(o))t[e]=K(e,a);return t}function de(o,t,e){var i;const a=t==null?void 0:t.BDOBlob;if(!(a!=null&&a.Activity))return null;const r=o==null?void 0:o.Id;for(const n of a.Activity)if(n.Id!==r&&(i=n.Input)!=null&&i[e])return n.Input[e];return null}function ye(o){return async(t,e,a)=>{const r={},i="_getFields"in o?o._getFields():o,n=(a==null?void 0:a.names)??Object.keys(i);for(const f of n){const w=i[f];if(!w||w.readOnly)continue;let s=t[f];typeof s=="string"&&w.meta.Type==="Number"&&(s=s===""?void 0:Number(s));const l=w.validate(s);if(!l.valid&&l.errors.length>0){r[f]={type:"validate",message:l.errors[0]||`${f} is invalid`};continue}const v=k.validateConstraints(w,s);!v.valid&&v.errors.length>0&&(r[f]={type:"constraint",message:v.errors[0]})}return Object.keys(r).length===0?{values:t,errors:{}}:{values:{},errors:r}}}function fe(o,t,e){const a=o._getFields(),r=new Map,i=`/api/app/process/${o.meta.businessProcessId}/${o.meta.activityId}`,n=y.createResourceClient(i);function f(){if(!e)throw new Error("Cannot perform attachment operation: no activity instance ID");return e}return new Proxy({},{get(w,s){if(typeof s=="symbol")return;if(s==="_id")return e;if(s==="toJSON")return()=>t.getValues();if(s==="validate")return()=>t.trigger();if(r.has(s))return r.get(s);const l=a[s],v=(l==null?void 0:l.meta)??{_id:s,Name:s,Type:"String"},E=(l==null?void 0:l.readOnly)??!1,I=()=>l?l.validate(t.getValues(s)):{valid:!0,errors:[]},q=$=>{const d=t.getValues(s);return d??$};if(!E){const $=()=>{const c=t.getValues(s);return v.Type==="File"?c??[]:c},d={label:(l==null?void 0:l.label)??s,required:(l==null?void 0:l.required)??!1,readOnly:!1,defaultValue:l==null?void 0:l.defaultValue,meta:v,get:$,getOrDefault:q,set:c=>{t.setValue(s,c,{shouldDirty:!0,shouldTouch:!0,shouldValidate:!1})},validate:I};return(v.Type==="Image"||v.Type==="File")&&(v.Type==="Image"?(d.upload=async c=>{z.validateFileExtension(c.name,"Image");const m=f(),[g]=await n.getUploadUrl(m,s,[{FileName:c.name,Size:c.size,FileExtension:z.extractFileExtension(c.name)}]);await fetch(g.UploadUrl.URL,{method:"PUT",headers:{"Content-Type":g.ContentType},body:c});const F={_id:g._id,_name:g._name,FileName:g.FileName,FileExtension:g.FileExtension,Size:g.Size,ContentType:g.ContentType};return t.setValue(s,F,{shouldDirty:!0}),F},d.deleteAttachment=async()=>{const c=t.getValues(s),m=f();if(!(c!=null&&c._id))throw new Error(`${s} has no image to delete`);await n.deleteAttachment(m,s,c._id),t.setValue(s,null,{shouldDirty:!0})},d.getDownloadUrl=async c=>{const m=t.getValues(s),g=f();if(!(m!=null&&m._id))throw new Error(`${s} has no image`);return n.getDownloadUrl(g,s,m._id,c)}):(d.upload=async c=>{for(const O of c)z.validateFileExtension(O.name,"File");const m=f(),g=c.map(O=>({FileName:O.name,Size:O.size,FileExtension:z.extractFileExtension(O.name)})),F=await n.getUploadUrl(m,s,g),R=await Promise.all(c.map(async(O,P)=>(await fetch(F[P].UploadUrl.URL,{method:"PUT",headers:{"Content-Type":F[P].ContentType},body:O}),{_id:F[P]._id,_name:F[P]._name,FileName:F[P].FileName,FileExtension:F[P].FileExtension,Size:F[P].Size,ContentType:F[P].ContentType}))),u=t.getValues(s)??[];return t.setValue(s,[...u,...R],{shouldDirty:!0}),R},d.deleteAttachment=async c=>{const m=t.getValues(s)??[],g=f();await n.deleteAttachment(g,s,c),t.setValue(s,m.filter(F=>F._id!==c),{shouldDirty:!0})},d.getDownloadUrl=async(c,m)=>{const g=f();return n.getDownloadUrl(g,s,c,m)},d.getDownloadUrls=async c=>{const m=f();return n.getDownloadUrls(m,s,c)})),r.set(s,d),d}const B=()=>{const $=t.getValues(s);return v.Type==="File"?$??[]:$},D={label:(l==null?void 0:l.label)??s,required:(l==null?void 0:l.required)??!1,readOnly:!0,defaultValue:l==null?void 0:l.defaultValue,meta:v,get:B,getOrDefault:q,validate:I};return(v.Type==="Image"||v.Type==="File")&&(v.Type==="Image"?D.getDownloadUrl=async $=>{const d=t.getValues(s),c=f();if(!(d!=null&&d._id))throw new Error(`${s} has no image to download`);return n.getDownloadUrl(c,s,d._id,$)}:(D.getDownloadUrl=async($,d)=>{const c=f();return n.getDownloadUrl(c,s,$,d)},D.getDownloadUrls=async $=>{const d=f();return n.getDownloadUrls(d,s,$)})),r.set(s,D),D},has(w,s){return typeof s=="symbol"?!1:s==="_id"||s==="toJSON"||s==="validate"?!0:s in a},ownKeys(w){return[...Object.keys(a),"_id","toJSON","validate"]},getOwnPropertyDescriptor(w,s){if(typeof s!="symbol")return{configurable:!0,enumerable:s!=="toJSON"&&s!=="validate"}}})}function ge(o,t){const{activity_instance_id:e,defaultValues:a={},mode:r="onBlur",enabled:i=!0}=t,[n,f]=h.useState(!1),[w,s]=h.useState(null),[l,v]=h.useState(!1),[E,I]=h.useState({}),q=h.useRef(!1),B=h.useRef(null),{data:D,isLoading:$}=oe.useQuery({queryKey:["bp-metadata",o.meta.businessProcessId],queryFn:()=>ce.getBdoSchema(o.meta.businessProcessId),staleTime:30*60*1e3}),d=h.useMemo(()=>{const _=D==null?void 0:D.BDOBlob;return _!=null&&_.Activity?_.Activity.find(b=>b.Id===o.meta.activityId)??null:null},[D,o.meta.activityId]),c=$,m=h.useMemo(()=>d!=null&&d.Input?Y(d.Input):o._getFields(),[d,o]),g=h.useMemo(()=>({...E,...m}),[E,m]),F=h.useMemo(()=>Object.keys(g).filter(_=>g[_].readOnly),[g]),R=h.useMemo(()=>ye(m),[m]),u=k.useForm({mode:r,defaultValues:a,resolver:R}),O=h.useMemo(()=>o._getOps(),[o]),P=h.useMemo(()=>fe(o,u,e),[o,u,e]);h.useEffect(()=>{if(!i||c)return;let _=!0;return(async()=>{f(!0),s(null);try{const T=await O.read(e);if(!_)return;if(T&&typeof T=="object"){const U=k.coerceRecordForForm(m,T),p={...a,...U};(B.current===null||T!==B.current)&&(u.reset(p),B.current=T);const M=new Set(["_id","BPInstanceId","Status","AssignedTo","CompletedAt","_created_at","_modified_at","_created_by","_modified_by","_v","_m_v"]),x={};for(const C of Object.keys(T))if(!m[C]&&!M.has(C)){const S=de(d,D,C);S&&(x[C]=K(C,{...S,ReadOnly:!0}))}Object.keys(x).length>0&&I(x)}}catch(T){if(!_)return;console.error("Failed to read activity data:",T),s(G.toError(T))}finally{_&&f(!1)}})(),()=>{_=!1}},[i,c,O,e]);const Q=h.useCallback((_,b)=>O.update(e,{[_]:b}),[O,e]),J=h.useMemo(()=>k.createSyncField({apiFn:Q,allFields:g,readonlyFieldNames:F,rhf:u,isComputingRef:q}),[Q,g,F,u]),L=r==="onChange"||r==="all",H=r==="onBlur"||r==="onTouched"||r==="all",Z=h.useMemo(()=>k.createEnhancedRegister({rhf:u,allFields:g,syncField:J,syncOnBlur:H,syncOnChange:L}),[u,g,J,H,L]),ee=h.useMemo(()=>k.createEnhancedControl({control:u.control,syncField:J,syncOnBlur:H,syncOnChange:L}),[u.control,J,H,L]),te=h.useCallback((_,b)=>u.handleSubmit(async(T,U)=>{v(!0);try{const p={},M=new Set(F),x=u.formState.dirtyFields,C=u.getValues();for(const S of Object.keys(C)){if(M.has(S)||!x[S])continue;const N=C[S]!==void 0?C[S]:T[S],j=g[S];p[S]=j?k.coerceFieldValue(j,N):N}Object.keys(p).length>0&&await O.update(e,p),await(_==null?void 0:_(T,U))}catch(p){b==null||b(G.toError(p),U)}finally{v(!1)}},(T,U)=>{b==null||b(T,U)}),[u,O,F,g,e]),se=h.useCallback((_,b)=>u.handleSubmit(async(T,U)=>{v(!0);try{const p={},M=new Set(F),x=u.formState.dirtyFields,C=u.getValues();for(const S of Object.keys(C)){if(M.has(S)||!x[S])continue;const N=C[S]!==void 0?C[S]:T[S],j=g[S];p[S]=j?k.coerceFieldValue(j,N):N}Object.keys(p).length>0&&await O.update(e,p),await O.complete(e),await(_==null?void 0:_(T,U))}catch(p){b==null||b(G.toError(p),U)}finally{v(!1)}},(T,U)=>{b==null||b(T,U)}),[u,O,F,g,e]),ae=h.useCallback(()=>{u.clearErrors()},[u]),re=!!w;return{item:P,activity:o,register:Z,handleSubmit:te,handleComplete:se,watch:u.watch,setValue:u.setValue,getValues:u.getValues,reset:u.reset,trigger:u.trigger,control:ee,errors:u.formState.errors,isValid:u.formState.isValid,isDirty:u.formState.isDirty,isSubmitting:u.formState.isSubmitting||l,isSubmitSuccessful:u.formState.isSubmitSuccessful,isLoading:n||c,isMetadataLoading:c,loadError:w,hasError:re,bpMetadata:D??null,clearErrors:ae}}function he(o){const{activity:t,status:e,...a}=o,{businessProcessId:r,activityId:i}=t.meta,n=h.useMemo(()=>t._getOps(),[t]),f=h.useMemo(()=>e==="inprogress"?s=>n.inProgressList(s):s=>n.completedList(s),[n,e]),w=h.useMemo(()=>e==="inprogress"?s=>n.inProgressCount(s):s=>n.completedCount(s),[n,e]);return G.useTable({queryKey:["activity-table",r,i,e],listFn:f,countFn:w,...a})}const me={InProgress:"inprogress",Completed:"completed"};exports.Activity=ue;exports.ActivityInstance=X;exports.ActivityTableStatus=me;exports.Workflow=W;exports.buildFieldsFromInput=Y;exports.createFieldFromMeta=K;exports.useActivityForm=ge;exports.useActivityTable=he;
package/dist/workflow.mjs CHANGED
@@ -2,7 +2,7 @@ var oe = Object.defineProperty;
2
2
  var ce = (o, t, e) => t in o ? oe(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
3
3
  var N = (o, t, e) => ce(o, typeof t != "symbol" ? t + "" : t, e);
4
4
  import { c as S, g as $, d as le } from "./client-_ayziI1d.js";
5
- import { B as ue, c as de, S as Q, F as ye, U as fe, R as he, T as ge, D as me, b as _e, a as we, N as Fe } from "./FileField-BWrSHNRq.js";
5
+ import { B as ue, c as de, S as Q, F as ye, U as fe, R as he, T as ge, D as me, b as _e, a as we, N as Fe } from "./FileField-CZjS2uLh.js";
6
6
  import { useState as z, useRef as W, useMemo as P, useEffect as Te, useCallback as G } from "react";
7
7
  import { v as ve, u as Oe, b as be, d as Se, e as $e, f as X, a as Ce } from "./shared-5a7UkED1.js";
8
8
  import { useQuery as pe } from "@tanstack/react-query";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ram_28/kf-ai-sdk",
3
- "version": "2.0.20-beta.0",
3
+ "version": "2.0.20-beta.2",
4
4
  "description": "Type-safe, AI-driven SDK for building modern web applications with role-based access control",
5
5
  "author": "Ramprasad",
6
6
  "license": "MIT",
@@ -196,18 +196,8 @@ export class Item<T extends Record<string, unknown>> {
196
196
  }) as Item<T>;
197
197
  }
198
198
 
199
- /**
200
- * Require instanceId or throw.
201
- * TODO: Support create flow via draftInteraction to get temp _id
202
- */
203
199
  private _requireInstanceId(): string {
204
- const id = this._data._id as string | undefined;
205
- if (!id) {
206
- throw new Error(
207
- "Cannot perform attachment operation: item has no _id. Save the item first.",
208
- );
209
- }
210
- return id;
200
+ return (this._data._id as string | undefined) || "draft";
211
201
  }
212
202
 
213
203
  /**
@@ -105,6 +105,40 @@ function evaluateSystemIdentifier(
105
105
  return systemValue;
106
106
  }
107
107
 
108
+ /**
109
+ * ISO date regex: matches YYYY-MM-DD with optional time component
110
+ */
111
+ const _DATE_RE = /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}(:\d{2})?)?$/;
112
+
113
+ /**
114
+ * Convert a date-like value to a comparable timestamp (ms since epoch).
115
+ * Returns NaN if the value is not a valid date.
116
+ */
117
+ function _toTimestamp(value: unknown): number {
118
+ if (value instanceof Date) return value.getTime();
119
+ if (typeof value === "string" && _DATE_RE.test(value)) {
120
+ // Append "T00:00:00" for date-only strings to ensure consistent parsing
121
+ const normalized = value.includes("T") || value.includes(" ")
122
+ ? value.replace(" ", "T")
123
+ : value + "T00:00:00";
124
+ return new Date(normalized).getTime();
125
+ }
126
+ return NaN;
127
+ }
128
+
129
+ /**
130
+ * Compare two values for ordering operators (<, <=, >, >=).
131
+ * Uses date comparison for date-like values, numeric comparison otherwise.
132
+ */
133
+ function _compareValues(left: unknown, right: unknown): { l: number; r: number } {
134
+ const lt = _toTimestamp(left);
135
+ const rt = _toTimestamp(right);
136
+ // If both are valid dates, compare as timestamps
137
+ if (!isNaN(lt) && !isNaN(rt)) return { l: lt, r: rt };
138
+ // Otherwise fall back to numeric comparison
139
+ return { l: Number(left), r: Number(right) };
140
+ }
141
+
108
142
  /**
109
143
  * Evaluate a BinaryExpression node
110
144
  *
@@ -131,15 +165,23 @@ function evaluateBinaryExpression(
131
165
  case "!=":
132
166
  return left != right;
133
167
 
134
- // Numeric comparison operators
135
- case "<":
136
- return Number(left) < Number(right);
137
- case "<=":
138
- return Number(left) <= Number(right);
139
- case ">":
140
- return Number(left) > Number(right);
141
- case ">=":
142
- return Number(left) >= Number(right);
168
+ // Ordering comparison — date-aware
169
+ case "<": {
170
+ const { l, r } = _compareValues(left, right);
171
+ return l < r;
172
+ }
173
+ case "<=": {
174
+ const { l, r } = _compareValues(left, right);
175
+ return l <= r;
176
+ }
177
+ case ">": {
178
+ const { l, r } = _compareValues(left, right);
179
+ return l > r;
180
+ }
181
+ case ">=": {
182
+ const { l, r } = _compareValues(left, right);
183
+ return l >= r;
184
+ }
143
185
 
144
186
  // Arithmetic operators
145
187
  case "+":
@@ -71,7 +71,7 @@ export class ReferenceField<TRef = unknown> extends BaseField<
71
71
  * Fetch referenced records from the backend via the fetchField API.
72
72
  * Requires the field to be bound to a parent BDO.
73
73
  */
74
- async fetchOptions(instanceId: string): Promise<TRef[]> {
74
+ async fetchOptions(instanceId: string = "draft"): Promise<TRef[]> {
75
75
  if (!this._parentBoId) {
76
76
  throw new Error(
77
77
  `Field ${this.id} not bound to a BDO. Cannot fetch options.`
@@ -57,7 +57,7 @@ export class SelectField<T extends string | number = string> extends BaseField<T
57
57
  /**
58
58
  * Fetch dynamic options from the backend, returned as typed SelectOption[]
59
59
  */
60
- async fetchOptions(instanceId: string): Promise<SelectOptionType<T>[]> {
60
+ async fetchOptions(instanceId: string = "draft"): Promise<SelectOptionType<T>[]> {
61
61
  if (!this._parentBoId) {
62
62
  throw new Error(
63
63
  `Field ${this.id} not bound to a BDO. Cannot fetch options.`
@@ -32,7 +32,7 @@ export class UserField extends BaseField<UserFieldType> {
32
32
  * Fetch user records from the backend via the fetchField API.
33
33
  * Requires the field to be bound to a parent BDO.
34
34
  */
35
- async fetchOptions(instanceId: string): Promise<UserFieldType[]> {
35
+ async fetchOptions(instanceId: string = "draft"): Promise<UserFieldType[]> {
36
36
  if (!this._parentBoId) {
37
37
  throw new Error(
38
38
  `Field ${this.id} not bound to a BDO. Cannot fetch options.`,
@@ -21,9 +21,8 @@ import type {
21
21
  * Key principle: Item has NO state. It's a view over RHF's state.
22
22
  * Editable fields get set(), readonly fields do not.
23
23
  *
24
- * Draft-based upload: In create mode (no _id), upload() automatically creates
25
- * a draft record via draftInteraction() to get an _id, then uploads immediately.
26
- * On form submit, if a draft _id exists, update() is used instead of create().
24
+ * In create mode (no _id), uses "draft" as the instanceId for attachment
25
+ * uploads and fetch operations — no draftInteraction() call needed.
27
26
  *
28
27
  * @param bdo - The BDO instance for field metadata
29
28
  * @param form - The RHF useForm return object
@@ -36,34 +35,9 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
36
35
  const fields = bdo.getFields();
37
36
  const accessorCache = new Map<string, EditableFormFieldAccessorType<unknown> | ReadonlyFormFieldAccessorType<unknown>>();
38
37
 
39
- // Draft tracking for create mode — shared across all attachment fields in this form
40
38
  const boIdShared = bdo.getBoId();
41
- let draftId: string | null = null;
42
- let draftPromise: Promise<string> | null = null;
43
-
44
- /**
45
- * Ensures a record _id exists for attachment uploads.
46
- * In edit mode, returns the existing _id.
47
- * In create mode, creates a draft record via draftInteraction() to get an _id.
48
- * The draft _id is shared across all attachment fields and only created once.
49
- */
50
- async function ensureDraft(): Promise<string> {
51
- // If form already has an _id (edit mode or previous draft), use it
52
- const existing = form.getValues("_id" as Path<FieldValues>) as string | undefined;
53
- if (existing) return existing;
54
- if (draftId) return draftId;
55
- if (!draftPromise) {
56
- draftPromise = api(boIdShared).draftInteraction({}).then((d: any) => {
57
- draftId = d._id;
58
- form.setValue("_id" as Path<FieldValues>, draftId as any, { shouldDirty: false });
59
- return draftId!;
60
- }).catch((err: Error) => {
61
- draftPromise = null;
62
- throw err;
63
- });
64
- }
65
- return draftPromise;
66
- }
39
+ const getInstanceId = (): string =>
40
+ (form.getValues("_id" as Path<FieldValues>) as string) || "draft";
67
41
 
68
42
  return new Proxy({} as FormItemType<ExtractEditableType<B>, ExtractReadonlyType<B>>, {
69
43
  get(_, prop: string | symbol) {
@@ -87,11 +61,6 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
87
61
  return () => form.trigger();
88
62
  }
89
63
 
90
- // Internal: check if a draft was created (used by handleSubmit)
91
- if (prop === "_hasDraft") {
92
- return () => !!draftId;
93
- }
94
-
95
64
  // Return cached accessor if available
96
65
  if (accessorCache.has(prop)) {
97
66
  return accessorCache.get(prop);
@@ -179,21 +148,15 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
179
148
  validate,
180
149
  };
181
150
 
182
- // Enrich Image/File field accessors with attachment methods (draft-based upload)
151
+ // Enrich Image/File field accessors with attachment methods
183
152
  if (fieldMeta.Type === "Image" || fieldMeta.Type === "File") {
184
153
  const boId = boIdShared;
185
- const requireInstanceId = (): string => {
186
- const id = form.getValues("_id" as Path<FieldValues>) as string | undefined;
187
- if (!id) throw new Error("Save the record before attachment operations");
188
- return id;
189
- };
190
154
 
191
155
  if (fieldMeta.Type === "Image") {
192
- // Image: single file upload — always uploads immediately (draft in create mode)
156
+ // Image: single file upload
193
157
  (accessor as any).upload = async (file: File): Promise<FileType> => {
194
158
  validateFileExtension(file.name, "Image");
195
- const id = await ensureDraft();
196
-
159
+ const id = getInstanceId();
197
160
  const [uploadInfo] = await api(boId).getUploadUrl(id, prop, [
198
161
  { FileName: file.name, Size: file.size, FileExtension: extractFileExtension(file.name) },
199
162
  ]);
@@ -216,7 +179,7 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
216
179
 
217
180
  (accessor as any).deleteAttachment = async (): Promise<void> => {
218
181
  const val = form.getValues(prop as Path<FieldValues>) as any;
219
- const instanceId = requireInstanceId();
182
+ const instanceId = getInstanceId();
220
183
  if (!(val?._id)) throw new Error(`${prop} has no image to delete`);
221
184
  await api(boId).deleteAttachment(instanceId, prop, val._id);
222
185
  form.setValue(prop as Path<FieldValues>, null as any, { shouldDirty: true });
@@ -224,16 +187,15 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
224
187
 
225
188
  (accessor as any).getDownloadUrl = async (viewType?: AttachmentViewType): Promise<FileDownloadResponseType> => {
226
189
  const val = form.getValues(prop as Path<FieldValues>) as any;
227
- const instanceId = requireInstanceId();
190
+ const instanceId = getInstanceId();
228
191
  if (!(val?._id)) throw new Error(`${prop} has no image`);
229
192
  return api(boId).getDownloadUrl(instanceId, prop, val._id, viewType);
230
193
  };
231
194
  } else {
232
- // File field — multi-file, always uploads immediately (draft in create mode)
195
+ // File field — multi-file
233
196
  (accessor as any).upload = async (files: File[]): Promise<FileType[]> => {
234
197
  for (const file of files) validateFileExtension(file.name, "File");
235
- const id = await ensureDraft();
236
-
198
+ const id = getInstanceId();
237
199
  const requests = files.map((file) => ({
238
200
  FileName: file.name,
239
201
  Size: file.size,
@@ -264,7 +226,7 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
264
226
 
265
227
  (accessor as any).deleteAttachment = async (attachmentId: string): Promise<void> => {
266
228
  const current = (form.getValues(prop as Path<FieldValues>) as any[]) ?? [];
267
- const instanceId = requireInstanceId();
229
+ const instanceId = getInstanceId();
268
230
  await api(boId).deleteAttachment(instanceId, prop, attachmentId);
269
231
  form.setValue(
270
232
  prop as Path<FieldValues>,
@@ -277,13 +239,13 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
277
239
  attachmentId: string,
278
240
  viewType?: AttachmentViewType,
279
241
  ): Promise<FileDownloadResponseType> => {
280
- const instanceId = requireInstanceId();
242
+ const instanceId = getInstanceId();
281
243
  return api(boId).getDownloadUrl(instanceId, prop, attachmentId, viewType);
282
244
  };
283
245
  (accessor as any).getDownloadUrls = async (
284
246
  viewType?: AttachmentViewType,
285
247
  ): Promise<FileDownloadResponseType[]> => {
286
- const instanceId = requireInstanceId();
248
+ const instanceId = getInstanceId();
287
249
  return api(boId).getDownloadUrls(instanceId, prop, viewType);
288
250
  };
289
251
  }
@@ -314,16 +276,11 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
314
276
  // Enrich readonly Image/File field accessors with download methods
315
277
  if (fieldMeta.Type === "Image" || fieldMeta.Type === "File") {
316
278
  const boId = boIdShared;
317
- const requireInstanceId = (): string => {
318
- const id = form.getValues("_id" as Path<FieldValues>) as string | undefined;
319
- if (!id) throw new Error("Cannot perform attachment operation: item has no _id. Save the item first.");
320
- return id;
321
- };
322
279
 
323
280
  if (fieldMeta.Type === "Image") {
324
281
  (accessor as any).getDownloadUrl = async (viewType?: AttachmentViewType): Promise<FileDownloadResponseType> => {
325
282
  const val = form.getValues(prop as Path<FieldValues>) as any;
326
- const instanceId = requireInstanceId();
283
+ const instanceId = getInstanceId();
327
284
  if (!(val?._id)) throw new Error(`${prop} has no image to download`);
328
285
  return api(boId).getDownloadUrl(instanceId, prop, val._id, viewType);
329
286
  };
@@ -332,13 +289,13 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
332
289
  attachmentId: string,
333
290
  viewType?: AttachmentViewType,
334
291
  ): Promise<FileDownloadResponseType> => {
335
- const instanceId = requireInstanceId();
292
+ const instanceId = getInstanceId();
336
293
  return api(boId).getDownloadUrl(instanceId, prop, attachmentId, viewType);
337
294
  };
338
295
  (accessor as any).getDownloadUrls = async (
339
296
  viewType?: AttachmentViewType,
340
297
  ): Promise<FileDownloadResponseType[]> => {
341
- const instanceId = requireInstanceId();
298
+ const instanceId = getInstanceId();
342
299
  return api(boId).getDownloadUrls(instanceId, prop, viewType);
343
300
  };
344
301
  }
@@ -352,8 +309,6 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
352
309
  if (typeof prop === "symbol") return false;
353
310
  if (prop === "_id" || prop === "toJSON" || prop === "validate")
354
311
  return true;
355
- if (prop === "_hasDraft")
356
- return true;
357
312
  return prop in fields;
358
313
  },
359
314
 
@@ -365,7 +320,7 @@ export function createItemProxy<B extends BaseBdo<any, any, any>>(
365
320
  if (typeof prop === "symbol") return undefined;
366
321
  return {
367
322
  configurable: true,
368
- enumerable: prop !== "toJSON" && prop !== "validate" && prop !== "_hasDraft",
323
+ enumerable: prop !== "toJSON" && prop !== "validate",
369
324
  };
370
325
  },
371
326
  });