@memberjunction/graphql-dataprovider 5.36.0 → 5.38.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.md CHANGED
@@ -637,7 +637,7 @@ console.log(prompt.PromptRole); // From AIPrompt
637
637
  ### Related Documentation
638
638
 
639
639
  For comprehensive information about IS-A relationships in MemberJunction:
640
- - [IS-A Relationships Architecture](../../MJCore/docs/isa-relationships.md) - Server-side implementation details, entity structure, and transaction management
640
+ - [IS-A Relationships Architecture](../MJCore/docs/isa-relationships.md) - Server-side implementation details, entity structure, and transaction management
641
641
 
642
642
  ## Key Classes and Types
643
643
 
@@ -722,7 +722,7 @@ For the warm-load smart-cache-check flow this matters a lot — the client reads
722
722
 
723
723
  `setupGraphQLClient` orchestrates a deterministic warm-load path: after `provider.Config(...)` loads the cached `AllMetadata` blob from IndexedDB (gzip-compressed, three keys read in a single batched call), it calls `provider.preValidateAndRefresh()` to confirm the cache is current via a single batched timestamp round-trip. If current, engines trust their local caches and route per-view requests through `RunViewsWithCacheCheck` — a fingerprint-only GraphQL call that returns either a "current" marker (use local cache) or fresh data for stale entries.
724
724
 
725
- For the full architecture — differential updates, Redis cross-server sync, session-based deduplication, and deployment topologies — see the [**Caching & Pub/Sub Guide**](/guides/CACHING_AND_PUBSUB_GUIDE.md).
725
+ For the full architecture — differential updates, Redis cross-server sync, session-based deduplication, and deployment topologies — see the [**Caching & Pub/Sub Guide**](../../guides/CACHING_AND_PUBSUB_GUIDE.md).
726
726
 
727
727
  ## Dependencies
728
728
 
package/dist/index.cjs CHANGED
@@ -6,7 +6,7 @@
6
6
  ResultsJSON
7
7
  }
8
8
  }
9
- `,t=[];for(const n of this.PendingTransactions)t.push({EntityName:n.BaseEntity.EntityInfo.Name,EntityObjectJSON:await n.BaseEntity.GetDataObjectJSON(),OperationType:n.OperationType});const r={group:{Items:t,Variables:this.Variables.map(n=>({Name:n.Name,ItemIndex:this.MapVariableEntityObjectToPosition(n),FieldName:n.FieldName,Type:n.Type}))}},s=await this._provider.ExecuteGQL(e,r);if(s&&s.ExecuteTransactionGroup){const n=s.ExecuteTransactionGroup,a=[];for(let i=0;i<this.PendingTransactions.length;i++){const o=n.ResultsJSON[i],d=D.SafeJSONParse(o),l=this.PendingTransactions[i];a.push(new c.TransactionResult(l,d,d!==null))}return a}else throw new Error("Failed to execute transaction group")}}const z=900*1e3;class P{static{y(this,"FireAndForgetHelper")}static async Execute(e){const t=e.operationLabel??e.mutationFieldName;let r,s;try{const{promise:n,resolve:a,reject:i}=P.createCompletionPromise();s=P.setupTimeout(i,e.timeoutMs,e.timeoutErrorMessage),r=P.subscribeToPubSub(e,a,s);const o=await P.executeMutation(e);if(!e.validateAck(o)){s&&clearTimeout(s);const d=o?.errorMessage??"Server rejected the request";return c.LogError(`[FireAndForget:${t}] Server rejected: ${d}`),e.createErrorResult(d)}return c.LogStatus(`[FireAndForget:${t}] Server accepted, waiting for completion via WebSocket`),await n}catch(n){s&&clearTimeout(s);const a=n;throw c.LogError(`[FireAndForget:${t}] Error: ${a.message}`),n}finally{s&&clearTimeout(s),r&&r.unsubscribe()}}static createCompletionPromise(){let e,t;return{promise:new Promise((s,n)=>{e=s,t=n}),resolve:e,reject:t}}static setupTimeout(e,t,r){const s=t??z,n=r??"The operation may still be running on the server but the client timed out after 15 minutes. Please refresh the page to check the latest status.";return setTimeout(()=>{e(new Error(n))},s)}static subscribeToPubSub(e,t,r){const s=e.dataProvider.sessionId,n=e.operationLabel??e.mutationFieldName;return e.dataProvider.PushStatusUpdates(s).subscribe(a=>{try{const i=JSON.parse(a);e.onMessage&&e.onMessage(i),e.isCompletionEvent(i)&&(clearTimeout(r),c.LogStatus(`[FireAndForget:${n}] Completion event received`),t(e.extractResult(i)))}catch(i){console.error(`[FireAndForget:${n}] Failed to parse PubSub message:`,i)}})}static async executeMutation(e){return(await e.dataProvider.ExecuteGQL(e.mutation,e.variables))[e.mutationFieldName]}}class G{static{y(this,"GraphQLAIClient")}constructor(e){this._dataProvider=e}async RunAIPrompt(e){try{const t=u.gql`
9
+ `,t=[];for(const n of this.PendingTransactions)t.push({EntityName:n.BaseEntity.EntityInfo.Name,EntityObjectJSON:await n.BaseEntity.GetDataObjectJSON(),OperationType:n.OperationType});const r={group:{Items:t,Variables:this.Variables.map(n=>({Name:n.Name,ItemIndex:this.MapVariableEntityObjectToPosition(n),FieldName:n.FieldName,Type:n.Type}))}},s=await this._provider.ExecuteGQL(e,r);if(s&&s.ExecuteTransactionGroup){const n=s.ExecuteTransactionGroup,a=[];for(let i=0;i<this.PendingTransactions.length;i++){const o=n.ResultsJSON[i],d=D.SafeJSONParse(o),l=this.PendingTransactions[i];a.push(new c.TransactionResult(l,d,d!==null))}return a}else throw new Error("Failed to execute transaction group")}}const z=3600*1e3;class P{static{y(this,"FireAndForgetHelper")}static async Execute(e){const t=e.operationLabel??e.mutationFieldName;let r,s;try{const{promise:n,resolve:a,reject:i}=P.createCompletionPromise();s=P.setupTimeout(i,e.timeoutMs,e.timeoutErrorMessage),r=P.subscribeToPubSub(e,a,s);const o=await P.executeMutation(e);if(!e.validateAck(o)){s&&clearTimeout(s);const d=o?.errorMessage??"Server rejected the request";return c.LogError(`[FireAndForget:${t}] Server rejected: ${d}`),e.createErrorResult(d)}return c.LogStatus(`[FireAndForget:${t}] Server accepted, waiting for completion via WebSocket`),await n}catch(n){s&&clearTimeout(s);const a=n;throw c.LogError(`[FireAndForget:${t}] Error: ${a.message}`),n}finally{s&&clearTimeout(s),r&&r.unsubscribe()}}static createCompletionPromise(){let e,t;return{promise:new Promise((s,n)=>{e=s,t=n}),resolve:e,reject:t}}static setupTimeout(e,t,r){const s=t??z,n=r??"The operation may still be running on the server but the client timed out after 15 minutes. Please refresh the page to check the latest status.";return setTimeout(()=>{e(new Error(n))},s)}static subscribeToPubSub(e,t,r){const s=e.dataProvider.sessionId,n=e.operationLabel??e.mutationFieldName;return e.dataProvider.PushStatusUpdates(s).subscribe(a=>{try{const i=JSON.parse(a);e.onMessage&&e.onMessage(i),e.isCompletionEvent(i)&&(clearTimeout(r),c.LogStatus(`[FireAndForget:${n}] Completion event received`),t(e.extractResult(i)))}catch(i){console.error(`[FireAndForget:${n}] Failed to parse PubSub message:`,i)}})}static async executeMutation(e){return(await e.dataProvider.ExecuteGQL(e.mutation,e.variables))[e.mutationFieldName]}}class G{static{y(this,"GraphQLAIClient")}constructor(e){this._dataProvider=e}async RunAIPrompt(e){try{const t=u.gql`
10
10
  mutation RunAIPrompt(
11
11
  $promptId: String!,
12
12
  $data: String,
@@ -297,7 +297,7 @@
297
297
  }
298
298
  }
299
299
  }
300
- `,r={entityDocumentID:e.entityDocumentID};e.maxRecords!==void 0&&(r.maxRecords=e.maxRecords),e.filter!==void 0&&(r.filter=e.filter);const s=await this._dataProvider.ExecuteGQL(t,r);if(!s?.FetchEntityVectors)throw new Error("Invalid response from server");return s.FetchEntityVectors}catch(t){const r=t;return c.LogError("GraphQLAIClient.FetchEntityVectors failed",void 0,r),{Success:!1,Results:[],TotalCount:0,ElapsedMs:0,ErrorMessage:r.message||"Unknown error"}}}}const N="5.36.0",$="default";class U{static{y(this,"BrowserStorageProviderBase")}constructor(){this._storage=new Map}getCategoryMap(e){const t=e||$;let r=this._storage.get(t);return r||(r=new Map,this._storage.set(t,r)),r}async GetItem(e,t){const s=this.getCategoryMap(t||$).get(e);return s===void 0?null:s}async GetItems(e,t){const r=new Map;if(e.length===0)return r;const s=this.getCategoryMap(t||$);for(const n of new Set(e)){const a=s.get(n);r.set(n,a===void 0?null:a)}return r}async SetItem(e,t,r){this.getCategoryMap(r||$).set(e,t)}async Remove(e,t){this.getCategoryMap(t||$).delete(e)}async ClearCategory(e){const t=e||$;this._storage.delete(t)}async GetCategoryKeys(e){const t=this._storage.get(e||$);return t?Array.from(t.keys()):[]}}const H="MJ_Metadata",X=0;function Y(){try{const g=N.split("."),e=parseInt(g[0],10),t=parseInt(g[1],10);if(!Number.isFinite(e)||!Number.isFinite(t))throw new Error(`Could not parse major.minor from version "${N}"`);return e*1e3+t+X}catch(g){return c.LogErrorEx({error:g,message:"Failed to derive IDB version from PACKAGE_VERSION; using fallback 99999"}),99999}}y(Y,"computeIdbVersion");const Z=Y(),V=["mj:default","mj:Metadata","mj:RunViewCache","mj:RunQueryCache","mj:DatasetCache"];class _ extends U{static{y(this,"BrowserIndexedDBStorageProvider")}constructor(){super(),this._dbReady=!1,this.dbPromise=W.openDB(H,Z,{upgrade:y((e,t,r)=>{try{c.LogStatus(`[IDBCache] Upgrading IndexedDB schema v${t} \u2192 v${r} (package ${N}). Dropping all stores; caches will repopulate on first use.`);for(const s of Array.from(e.objectStoreNames))e.deleteObjectStore(s);for(const s of V)e.objectStoreNames.contains(s)||e.createObjectStore(s)}catch(s){c.LogErrorEx({error:s,message:s?.message})}},"upgrade"),blocked:y((e,t)=>{c.LogStatus(`[IDBCache] Upgrade from v${e} to v${t} blocked by another tab. Close other tabs to allow the upgrade to proceed.`)},"blocked")}),this.dbPromise.then(e=>{this._dbReady=!0,e.onversionchange=()=>{c.LogStatus("[IDBCache] DB schema upgraded in another tab \u2014 closing local connection."),e.close(),this._dbReady=!1}}).catch(e=>{c.LogErrorEx({error:e,message:"IndexedDB initialization failed: "+e?.message})})}get IsReady(){return this._dbReady}isKnownCategory(e){const t=`mj:${e}`;return V.includes(t)}getStoreName(e){const t=e||$;return this.isKnownCategory(t)?`mj:${t}`:"mj:default"}getStoreKey(e,t){const r=t||$;return this.isKnownCategory(r)?e:`[${r}]:${e}`}async SetItem(e,t,r){try{const s=await this.dbPromise,n=this.getStoreName(r),a=this.getStoreKey(e,r),i=s.transaction(n,"readwrite");await i.objectStore(n).put(t,a),await i.done}catch(s){c.LogErrorEx({error:s,message:s?.message})}}async GetItem(e,t){try{const r=await this.dbPromise,s=this.getStoreName(t),n=this.getStoreKey(e,t),a=await r.transaction(s).objectStore(s).get(n);return a===void 0?null:a}catch(r){return c.LogErrorEx({error:r,message:r?.message}),null}}async GetItems(e,t){const r=new Map;if(e.length===0)return r;try{const s=await this.dbPromise,n=this.getStoreName(t),a=Array.from(new Set(e)),i=a.map(E=>this.getStoreKey(E,t)),o=s.transaction(n,"readonly"),d=o.objectStore(n),l=i.map(E=>d.get(E)),f=await Promise.all(l);await o.done;for(let E=0;E<a.length;E++){const p=f[E];r.set(a[E],p===void 0?null:p)}return r}catch(s){c.LogErrorEx({error:s,message:s?.message});for(const n of new Set(e))r.set(n,null);return r}}async Remove(e,t){try{const r=await this.dbPromise,s=this.getStoreName(t),n=this.getStoreKey(e,t),a=r.transaction(s,"readwrite");await a.objectStore(s).delete(n),await a.done}catch(r){c.LogErrorEx({error:r,message:r?.message})}}async ClearCategory(e){try{const t=await this.dbPromise,r=e||$,s=this.getStoreName(e);if(this.isKnownCategory(r)){const n=t.transaction(s,"readwrite");await n.objectStore(s).clear(),await n.done}else{const n=`[${r}]:`,a=t.transaction("mj:default","readwrite"),i=a.objectStore("mj:default"),o=await i.getAllKeys();for(const d of o)typeof d=="string"&&d.startsWith(n)&&await i.delete(d);await a.done}}catch(t){c.LogErrorEx({error:t,message:t?.message})}}async GetCategoryKeys(e){try{const t=await this.dbPromise,r=e||$,s=this.getStoreName(e),i=await t.transaction(s,"readonly").objectStore(s).getAllKeys();if(this.isKnownCategory(r))return i.map(d=>String(d));const o=`[${r}]:`;return i.map(d=>String(d)).filter(d=>d.startsWith(o)).map(d=>d.slice(o.length))}catch(t){return c.LogErrorEx({error:t,message:t?.message}),[]}}}class ee extends c.ProviderConfigDataBase{static{y(this,"GraphQLProviderConfigData")}get Token(){return this.Data.Token}set Token(e){this.Data.Token=e}get MJAPIKey(){return this.Data.MJAPIKey}set MJAPIKey(e){this.Data.MJAPIKey=e}get UserAPIKey(){return this.Data.UserAPIKey}set UserAPIKey(e){this.Data.UserAPIKey=e}get URL(){return this.Data.URL}get WSURL(){return this.Data.WSURL}get RefreshTokenFunction(){return this.Data.RefreshFunction}get OnAuthenticationError(){return this.Data.OnAuthenticationError}set OnAuthenticationError(e){this.Data.OnAuthenticationError=e}constructor(e,t,r,s,n,a,i,o,d,l){super({Token:e,URL:t,WSURL:r,MJAPIKey:o,UserAPIKey:d,RefreshTokenFunction:s,OnAuthenticationError:l},n,a,i)}}class h extends c.ProviderBase{static{y(this,"GraphQLDataProvider")}constructor(){super(),this._refreshPromise=null,this._dynamicHeaders=new Map,this._innerCurrentUserQueryString=`CurrentUser {
300
+ `,r={entityDocumentID:e.entityDocumentID};e.maxRecords!==void 0&&(r.maxRecords=e.maxRecords),e.filter!==void 0&&(r.filter=e.filter);const s=await this._dataProvider.ExecuteGQL(t,r);if(!s?.FetchEntityVectors)throw new Error("Invalid response from server");return s.FetchEntityVectors}catch(t){const r=t;return c.LogError("GraphQLAIClient.FetchEntityVectors failed",void 0,r),{Success:!1,Results:[],TotalCount:0,ElapsedMs:0,ErrorMessage:r.message||"Unknown error"}}}}const N="5.38.0",$="default";class U{static{y(this,"BrowserStorageProviderBase")}constructor(){this._storage=new Map}getCategoryMap(e){const t=e||$;let r=this._storage.get(t);return r||(r=new Map,this._storage.set(t,r)),r}async GetItem(e,t){const s=this.getCategoryMap(t||$).get(e);return s===void 0?null:s}async GetItems(e,t){const r=new Map;if(e.length===0)return r;const s=this.getCategoryMap(t||$);for(const n of new Set(e)){const a=s.get(n);r.set(n,a===void 0?null:a)}return r}async SetItem(e,t,r){this.getCategoryMap(r||$).set(e,t)}async Remove(e,t){this.getCategoryMap(t||$).delete(e)}async ClearCategory(e){const t=e||$;this._storage.delete(t)}async GetCategoryKeys(e){const t=this._storage.get(e||$);return t?Array.from(t.keys()):[]}}const H="MJ_Metadata",X=0;function Y(){try{const g=N.split("."),e=parseInt(g[0],10),t=parseInt(g[1],10);if(!Number.isFinite(e)||!Number.isFinite(t))throw new Error(`Could not parse major.minor from version "${N}"`);return e*1e3+t+X}catch(g){return c.LogErrorEx({error:g,message:"Failed to derive IDB version from PACKAGE_VERSION; using fallback 99999"}),99999}}y(Y,"computeIdbVersion");const Z=Y(),V=["mj:default","mj:Metadata","mj:RunViewCache","mj:RunQueryCache","mj:DatasetCache"];class _ extends U{static{y(this,"BrowserIndexedDBStorageProvider")}constructor(){super(),this._dbReady=!1,this.dbPromise=W.openDB(H,Z,{upgrade:y((e,t,r)=>{try{c.LogStatus(`[IDBCache] Upgrading IndexedDB schema v${t} \u2192 v${r} (package ${N}). Dropping all stores; caches will repopulate on first use.`);for(const s of Array.from(e.objectStoreNames))e.deleteObjectStore(s);for(const s of V)e.objectStoreNames.contains(s)||e.createObjectStore(s)}catch(s){c.LogErrorEx({error:s,message:s?.message})}},"upgrade"),blocked:y((e,t)=>{c.LogStatus(`[IDBCache] Upgrade from v${e} to v${t} blocked by another tab. Close other tabs to allow the upgrade to proceed.`)},"blocked")}),this.dbPromise.then(e=>{this._dbReady=!0,e.onversionchange=()=>{c.LogStatus("[IDBCache] DB schema upgraded in another tab \u2014 closing local connection."),e.close(),this._dbReady=!1}}).catch(e=>{c.LogErrorEx({error:e,message:"IndexedDB initialization failed: "+e?.message})})}get IsReady(){return this._dbReady}isKnownCategory(e){const t=`mj:${e}`;return V.includes(t)}getStoreName(e){const t=e||$;return this.isKnownCategory(t)?`mj:${t}`:"mj:default"}getStoreKey(e,t){const r=t||$;return this.isKnownCategory(r)?e:`[${r}]:${e}`}async SetItem(e,t,r){try{const s=await this.dbPromise,n=this.getStoreName(r),a=this.getStoreKey(e,r),i=s.transaction(n,"readwrite");await i.objectStore(n).put(t,a),await i.done}catch(s){c.LogErrorEx({error:s,message:s?.message})}}async GetItem(e,t){try{const r=await this.dbPromise,s=this.getStoreName(t),n=this.getStoreKey(e,t),a=await r.transaction(s).objectStore(s).get(n);return a===void 0?null:a}catch(r){return c.LogErrorEx({error:r,message:r?.message}),null}}async GetItems(e,t){const r=new Map;if(e.length===0)return r;try{const s=await this.dbPromise,n=this.getStoreName(t),a=Array.from(new Set(e)),i=a.map(E=>this.getStoreKey(E,t)),o=s.transaction(n,"readonly"),d=o.objectStore(n),l=i.map(E=>d.get(E)),f=await Promise.all(l);await o.done;for(let E=0;E<a.length;E++){const p=f[E];r.set(a[E],p===void 0?null:p)}return r}catch(s){c.LogErrorEx({error:s,message:s?.message});for(const n of new Set(e))r.set(n,null);return r}}async Remove(e,t){try{const r=await this.dbPromise,s=this.getStoreName(t),n=this.getStoreKey(e,t),a=r.transaction(s,"readwrite");await a.objectStore(s).delete(n),await a.done}catch(r){c.LogErrorEx({error:r,message:r?.message})}}async ClearCategory(e){try{const t=await this.dbPromise,r=e||$,s=this.getStoreName(e);if(this.isKnownCategory(r)){const n=t.transaction(s,"readwrite");await n.objectStore(s).clear(),await n.done}else{const n=`[${r}]:`,a=t.transaction("mj:default","readwrite"),i=a.objectStore("mj:default"),o=await i.getAllKeys();for(const d of o)typeof d=="string"&&d.startsWith(n)&&await i.delete(d);await a.done}}catch(t){c.LogErrorEx({error:t,message:t?.message})}}async GetCategoryKeys(e){try{const t=await this.dbPromise,r=e||$,s=this.getStoreName(e),i=await t.transaction(s,"readonly").objectStore(s).getAllKeys();if(this.isKnownCategory(r))return i.map(d=>String(d));const o=`[${r}]:`;return i.map(d=>String(d)).filter(d=>d.startsWith(o)).map(d=>d.slice(o.length))}catch(t){return c.LogErrorEx({error:t,message:t?.message}),[]}}}class ee extends c.ProviderConfigDataBase{static{y(this,"GraphQLProviderConfigData")}get Token(){return this.Data.Token}set Token(e){this.Data.Token=e}get MJAPIKey(){return this.Data.MJAPIKey}set MJAPIKey(e){this.Data.MJAPIKey=e}get UserAPIKey(){return this.Data.UserAPIKey}set UserAPIKey(e){this.Data.UserAPIKey=e}get URL(){return this.Data.URL}get WSURL(){return this.Data.WSURL}get RefreshTokenFunction(){return this.Data.RefreshFunction}get OnAuthenticationError(){return this.Data.OnAuthenticationError}set OnAuthenticationError(e){this.Data.OnAuthenticationError=e}constructor(e,t,r,s,n,a,i,o,d,l){super({Token:e,URL:t,WSURL:r,MJAPIKey:o,UserAPIKey:d,RefreshTokenFunction:s,OnAuthenticationError:l},n,a,i)}}class h extends c.ProviderBase{static{y(this,"GraphQLDataProvider")}constructor(){super(),this._refreshPromise=null,this._dynamicHeaders=new Map,this._innerCurrentUserQueryString=`CurrentUser {
301
301
  ${this.userInfoString()}
302
302
  MJUserRoles_UserIDArray {
303
303
  ${this.userRoleInfoString()}
@@ -588,7 +588,7 @@
588
588
  }
589
589
  }`,r=await this.ExecuteGQL(t,{DataContextItemID:e});if(r&&r.GetDataContextItemData){if(r.GetDataContextItemData.Success)return JSON.parse(r.GetDataContextItemData.Result);throw new Error(r.GetDataContextItemData.ErrorMessage)}else throw new Error("GraphQL query failed")}catch(t){throw c.LogError(t),t}}static async ExecuteGQL(e,t,r=!0){return h.Instance.ExecuteGQL(e,t,r)}async ExecuteGQL(e,t,r=!0){try{return await this._client.request(e,t)}catch(s){if(console.error("[GraphQL] ExecuteGQL error caught:",{hasResponse:!!s?.response,hasErrors:!!s?.response?.errors,errorCount:s?.response?.errors?.length,firstError:s?.response?.errors?.[0],errorCode:s?.response?.errors?.[0]?.extensions?.code,errorMessage:s?.response?.errors?.[0]?.message,fullError:s}),s&&s.response&&s.response.errors?.length>0)if(s.response.errors[0]?.extensions?.code?.toUpperCase().trim()==="JWT_EXPIRED"){if(r)return await this.RefreshToken(),await this.ExecuteGQL(e,t,!1);throw c.LogError("JWT_EXPIRED and refreshTokenIfNeeded is false"),s}else throw s;else throw c.LogError(s),s}}async RefreshToken(){const e=h.Instance&&h.Instance._configData===this._configData;if(e&&h.Instance._refreshPromise)return h.Instance._refreshPromise;if(this._refreshPromise)return this._refreshPromise;console.log("[GraphQL] Starting token refresh..."),this._refreshPromise=this.performTokenRefresh(),e&&(h.Instance._refreshPromise=this._refreshPromise);try{await this._refreshPromise,console.log("[GraphQL] Token refresh completed successfully")}finally{this._refreshPromise=null,e&&h.Instance&&(h.Instance._refreshPromise=null)}}async performTokenRefresh(){if(this._configData.Data.RefreshTokenFunction)try{const e=await this._configData.Data.RefreshTokenFunction();if(e){this._configData.Token=e;const t=this.CreateNewGraphQLClient(this._configData.URL,this._configData.Token,this._sessionId,this._configData.MJAPIKey,this._configData.UserAPIKey);this._client=t,h.Instance&&h.Instance._configData===this._configData&&(h.Instance._client=t)}else{const t=new Error("Refresh token function returned null or undefined token");throw this.notifyAuthenticationError(t),t}}catch(e){const t=e instanceof Error?e:new Error(String(e));throw this.notifyAuthenticationError(t),e}else{const e=new Error("No refresh token function provided");throw this.notifyAuthenticationError(e),e}}notifyAuthenticationError(e){try{const t=this._configData?.OnAuthenticationError;t&&t(e)}catch(t){console.error("[GraphQLDataProvider] Error in OnAuthenticationError callback:",t)}}static async RefreshToken(){return h.Instance.RefreshToken()}static async clearClientCache(e=new Set){const t=[];for(let r=0;r<localStorage.length;r++){const s=localStorage.key(r);s&&!e.has(s)&&t.push(s)}t.forEach(r=>localStorage.removeItem(r)),await new Promise(r=>{const s=indexedDB.deleteDatabase("MJ_Metadata");s.onsuccess=()=>r(),s.onerror=()=>r(),s.onblocked=()=>r()})}CreateNewGraphQLClient(e,t,r,s,n){const a={"x-session-id":r};t&&(a.authorization="Bearer "+t),s&&(a["x-mj-api-key"]=s),n&&(a["x-api-key"]=n);const i=new u.GraphQLClient(e,{headers:a});for(const[o,d]of this._dynamicHeaders)i.setHeader(o,d);return i}userInfoString(){return this.infoString(new c.UserInfo(null,null))}userRoleInfoString(){return this.infoString(new c.UserRoleInfo(null))}infoString(e){let t="";const r=Object.keys(e);for(const s of r)s.startsWith("__mj_")?t+=s.replace("__mj_","_mj__")+`
590
590
  `:s.startsWith("_")||(t+=s+`
591
- `);return t}get LocalStorageProvider(){return this._localStorageProvider||(typeof indexedDB<"u"?this._localStorageProvider=new _:this._localStorageProvider=new c.InMemoryLocalStorageProvider),this._localStorageProvider}get Metadata(){return this}get SocketConnectivity$(){return this._socketStateSubject.asObservable()}get SocketConnectionState(){return this._socketStateSubject.value}ForceSocketReconnect(){this.disposeWSClient()}getOrCreateWSClient(){const e=Date.now();return this._wsClient&&this._wsClientCreatedAt&&e-this._wsClientCreatedAt>this.WS_CLIENT_MAX_AGE_MS&&this._activeSubscriptionCount===0&&this.disposeWSClient(),this._wsClient||(this._isDisposingSocketIntentionally=!1,this._wsClient=j.createClient({url:this.ConfigData.WSURL,connectionParams:{Authorization:"Bearer "+this.ConfigData.Token},keepAlive:3e4,retryAttempts:3,shouldRetry:y(()=>!0,"shouldRetry")}),this._wsClientCreatedAt=e,this._wsClient.on("connected",()=>{this._socketStateSubject.next("connected")}),this._wsClient.on("closed",()=>{this._isDisposingSocketIntentionally||this._socketStateSubject.next("disconnected")}),this._subscriptionCleanupTimer||(this._subscriptionCleanupTimer=setInterval(()=>{this.cleanupStaleSubscriptions()},this.SUBSCRIPTION_CLEANUP_INTERVAL_MS))),this._wsClient}disposeWSClient(){if(this._wsClient){this._isDisposingSocketIntentionally=!0;try{this._wsClient.dispose()}catch(e){console.error("[GraphQLDataProvider] Error disposing WebSocket client:",e)}this._wsClient=null,this._wsClientCreatedAt=null,this._socketStateSubject.next("unknown")}}completeAllSubjects(){this._pushStatusSubjects.forEach((e,t)=>{try{e.subject.complete(),e.subscription.unsubscribe()}catch(r){console.error(`[GraphQLDataProvider] Error cleaning up subject for ${t}:`,r)}}),this._pushStatusSubjects.clear()}cleanupStaleSubscriptions(){if(!this._isCleaningUp){this._isCleaningUp=!0;try{const e=Date.now(),t=this._pushStatusSubjects.size,r=Array.from(this._pushStatusSubjects.entries()),s=[];r.forEach(([n,a])=>{const i=e-a.lastRequestedAt,o=e-a.lastEmissionAt;a.activeSubscribers===0&&i>=this.SUBSCRIPTION_IDLE_TIMEOUT_MS&&o>=this.SUBSCRIPTION_IDLE_TIMEOUT_MS&&(console.log(`[GraphQLDataProvider] Marking session ${n} for cleanup: activeSubscribers=${a.activeSubscribers}, timeSinceRequested=${Math.round(i/1e3)}s, timeSinceEmission=${Math.round(o/1e3)}s`),s.push(n))}),s.forEach(n=>{const a=this._pushStatusSubjects.get(n);if(a)try{a.subject.complete(),a.subscription.unsubscribe(),this._pushStatusSubjects.delete(n),console.log(`[GraphQLDataProvider] Cleaned up stale subscription for session: ${n}`)}catch(i){console.error(`[GraphQLDataProvider] Error cleaning up subscription for ${n}:`,i)}}),s.length>0&&console.log(`[GraphQLDataProvider] Cleaned up ${s.length} stale subscription(s)`),this._pushStatusSubjects.size===0&&this._wsClient&&this._wsClientCreatedAt&&e-this._wsClientCreatedAt>this.WS_CLIENT_MAX_AGE_MS&&(console.log("[GraphQLDataProvider] Disposing of idle WebSocket client"),this.disposeWSClient())}finally{this._isCleaningUp=!1}}}subscribe(e,t){return new A.Observable(r=>{const s=this.getOrCreateWSClient();this._activeSubscriptionCount++;const n=s.subscribe({query:e,variables:t},{next:y(a=>{r.next(a.data)},"next"),error:y(async a=>{const i=a;if(i?.extensions?.code==="JWT_EXPIRED"||i?.message?.includes("token has expired")||i?.message?.includes("JWT_EXPIRED")){console.log("[GraphQLDataProvider] WebSocket JWT token expired, refreshing and reconnecting...");try{await this.RefreshToken(),this.disposeWSClient(),r.complete()}catch(d){console.error("[GraphQLDataProvider] Failed to refresh token for WebSocket:",d),r.error(d)}}else r.error(a)},"error"),complete:y(()=>{r.complete()},"complete")});return()=>{this._activeSubscriptionCount--,n()}})}PushStatusUpdates(e=null){e||(e=this.sessionId);const t=Date.now(),r=this._pushStatusSubjects.get(e);if(r)return r.lastRequestedAt=t,new A.Observable(o=>{r.activeSubscribers++;const d=r.subject.subscribe(o);return()=>{const l=this._pushStatusSubjects.get(e);l&&l.activeSubscribers>0&&l.activeSubscribers--,d.unsubscribe()}});const s=u.gql`subscription StatusUpdates($sessionId: String!) {
591
+ `);return t}get LocalStorageProvider(){return this._localStorageProvider||(typeof indexedDB<"u"?this._localStorageProvider=new _:this._localStorageProvider=new c.InMemoryLocalStorageProvider),this._localStorageProvider}get Metadata(){return this}get SocketConnectivity$(){return this._socketStateSubject.asObservable()}get SocketConnectionState(){return this._socketStateSubject.value}ForceSocketReconnect(){this.disposeWSClient()}getOrCreateWSClient(){const e=Date.now();return this._wsClient&&this._wsClientCreatedAt&&e-this._wsClientCreatedAt>this.WS_CLIENT_MAX_AGE_MS&&this._activeSubscriptionCount===0&&this.disposeWSClient(),this._wsClient||(this._isDisposingSocketIntentionally=!1,this._wsClient=j.createClient({url:this.ConfigData.WSURL,connectionParams:y(()=>({Authorization:"Bearer "+this.ConfigData.Token}),"connectionParams"),keepAlive:3e4,retryAttempts:3,shouldRetry:y(()=>!0,"shouldRetry")}),this._wsClientCreatedAt=e,this._wsClient.on("connected",()=>{this._socketStateSubject.next("connected")}),this._wsClient.on("closed",t=>{if(this._isDisposingSocketIntentionally)return;this._socketStateSubject.next("disconnected"),t?.code===4403&&this._configData.Data.RefreshTokenFunction&&this.RefreshToken().catch(()=>{})}),this._subscriptionCleanupTimer||(this._subscriptionCleanupTimer=setInterval(()=>{this.cleanupStaleSubscriptions()},this.SUBSCRIPTION_CLEANUP_INTERVAL_MS))),this._wsClient}disposeWSClient(){if(this._wsClient){this._isDisposingSocketIntentionally=!0;try{this._wsClient.dispose()}catch(e){console.error("[GraphQLDataProvider] Error disposing WebSocket client:",e)}this._wsClient=null,this._wsClientCreatedAt=null,this._socketStateSubject.next("unknown")}}completeAllSubjects(){this._pushStatusSubjects.forEach((e,t)=>{try{e.subject.complete(),e.subscription.unsubscribe()}catch(r){console.error(`[GraphQLDataProvider] Error cleaning up subject for ${t}:`,r)}}),this._pushStatusSubjects.clear()}cleanupStaleSubscriptions(){if(!this._isCleaningUp){this._isCleaningUp=!0;try{const e=Date.now(),t=this._pushStatusSubjects.size,r=Array.from(this._pushStatusSubjects.entries()),s=[];r.forEach(([n,a])=>{const i=e-a.lastRequestedAt,o=e-a.lastEmissionAt;a.activeSubscribers===0&&i>=this.SUBSCRIPTION_IDLE_TIMEOUT_MS&&o>=this.SUBSCRIPTION_IDLE_TIMEOUT_MS&&(console.log(`[GraphQLDataProvider] Marking session ${n} for cleanup: activeSubscribers=${a.activeSubscribers}, timeSinceRequested=${Math.round(i/1e3)}s, timeSinceEmission=${Math.round(o/1e3)}s`),s.push(n))}),s.forEach(n=>{const a=this._pushStatusSubjects.get(n);if(a)try{a.subject.complete(),a.subscription.unsubscribe(),this._pushStatusSubjects.delete(n),console.log(`[GraphQLDataProvider] Cleaned up stale subscription for session: ${n}`)}catch(i){console.error(`[GraphQLDataProvider] Error cleaning up subscription for ${n}:`,i)}}),s.length>0&&console.log(`[GraphQLDataProvider] Cleaned up ${s.length} stale subscription(s)`),this._pushStatusSubjects.size===0&&this._wsClient&&this._wsClientCreatedAt&&e-this._wsClientCreatedAt>this.WS_CLIENT_MAX_AGE_MS&&(console.log("[GraphQLDataProvider] Disposing of idle WebSocket client"),this.disposeWSClient())}finally{this._isCleaningUp=!1}}}subscribe(e,t){return new A.Observable(r=>{const s=this.getOrCreateWSClient();this._activeSubscriptionCount++;const n=s.subscribe({query:e,variables:t},{next:y(a=>{r.next(a.data)},"next"),error:y(async a=>{const i=a;if(i?.extensions?.code==="JWT_EXPIRED"||i?.message?.includes("token has expired")||i?.message?.includes("JWT_EXPIRED")){console.log("[GraphQLDataProvider] WebSocket JWT token expired, refreshing and reconnecting...");try{await this.RefreshToken(),this.disposeWSClient(),r.complete()}catch(d){console.error("[GraphQLDataProvider] Failed to refresh token for WebSocket:",d),r.error(d)}}else r.error(a)},"error"),complete:y(()=>{r.complete()},"complete")});return()=>{this._activeSubscriptionCount--,n()}})}PushStatusUpdates(e=null){e||(e=this.sessionId);const t=Date.now(),r=this._pushStatusSubjects.get(e);if(r)return r.lastRequestedAt=t,new A.Observable(o=>{r.activeSubscribers++;const d=r.subject.subscribe(o);return()=>{const l=this._pushStatusSubjects.get(e);l&&l.activeSubscribers>0&&l.activeSubscribers--,d.unsubscribe()}});const s=u.gql`subscription StatusUpdates($sessionId: String!) {
592
592
  statusUpdates(sessionId: $sessionId) {
593
593
  date
594
594
  message