@lomray/react-mobx-manager 3.2.0-beta.2 → 3.3.0-beta.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lomray/react-mobx-manager",
3
- "version": "3.2.0-beta.2",
3
+ "version": "3.3.0-beta.1",
4
4
  "description": "This package provides Mobx stores manager for react.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -8,6 +8,10 @@ interface ISuspenseQueryParams {
8
8
  fieldName?: string;
9
9
  errorFields?: string[];
10
10
  }
11
+ interface ISuspenseSubqueryOptions {
12
+ id: string;
13
+ hash: unknown;
14
+ }
11
15
  /**
12
16
  * Run request and cache promise
13
17
  * Sync suspense status between server and client
@@ -17,6 +21,13 @@ declare class SuspenseQuery {
17
21
  * @protected
18
22
  */
19
23
  protected promise: Promise<any> | undefined;
24
+ /**
25
+ * Subqueries info
26
+ */
27
+ protected subqueries: Map<string, {
28
+ hash: unknown;
29
+ promise?: IPromise<any>;
30
+ }>;
20
31
  /**
21
32
  * Target store
22
33
  */
@@ -62,6 +73,12 @@ declare class SuspenseQuery {
62
73
  * Save request resolve status
63
74
  */
64
75
  query: <TReturn>(promise: () => Promise<TReturn>) => TReturn | undefined;
76
+ /**
77
+ * Run subquery
78
+ * Re-fetch data from query by hash changes in children components
79
+ * NOTE: only client side
80
+ */
81
+ subquery: <TReturn>(promise: () => Promise<TReturn>, options: ISuspenseSubqueryOptions) => TReturn | undefined;
65
82
  /**
66
83
  * Change status of promise.
67
84
  * Throw promise to react suspense
package/suspense-query.js CHANGED
@@ -1,2 +1,2 @@
1
- import{extendObservable as e,observable as s,runInAction as r}from"mobx";class t{promise;store;params;constructor(r,{fieldName:t="sR",errorFields:i=["name","message"]}={}){this.store=r,this.params={fieldName:t,errorFields:i};const o=r.init?.bind(r);r.init=()=>{this.isComplete(),o?.()},e(r,{[t]:!1},{[t]:s})}errorJson(e){e.toJSON=()=>this.params.errorFields.reduce(((s,r)=>({...s,[r]:e?.[r]})),{})}jsonToError(e,s){return this.params.errorFields.forEach((r=>{e[r]=s?.[r]})),e}isComplete(){const e=this.store[this.params.fieldName];if("boolean"!==typeof e)throw this.jsonToError(new Error(e?.message??e?.name),e);return!0===e}query=e=>{const{fieldName:s}=this.params;if(!this.isComplete())return this.promise||(this.promise=e(),this.promise.then((()=>{r((()=>{this.store[s]=!0}))}),(e=>{r((()=>{this.errorJson(e),this.store[s]=e}))}))),t.run(this.promise)};static run=e=>{if(e){switch(e.status){case"fulfilled":return e.value;case"pending":throw e;case"rejected":throw e.reason;default:e.status="pending",e.then((s=>{e.status="fulfilled",e.value=s}),(s=>{e.status="rejected",e.reason=s}))}throw e}}}export{t as default};
1
+ import{extendObservable as s,observable as e,runInAction as r}from"mobx";class t{promise;subqueries=new Map;store;params;constructor(r,{fieldName:t="sR",errorFields:i=["name","message"]}={}){this.store=r,this.params={fieldName:t,errorFields:i};const o=r.init?.bind(r);r.init=()=>{this.isComplete(),o?.()},s(r,{[t]:!1},{[t]:e})}errorJson(s){s.toJSON=()=>this.params.errorFields.reduce(((e,r)=>({...e,[r]:s?.[r]})),{})}jsonToError(s,e){return this.params.errorFields.forEach((r=>{s[r]=e?.[r]})),s}isComplete(){const s=this.store[this.params.fieldName];if("boolean"!==typeof s)throw this.jsonToError(new Error(s?.message??s?.name),s);return!0===s}query=s=>{const{fieldName:e}=this.params;if(!this.isComplete())return this.promise||(this.promise=s(),this.promise.then((()=>{r((()=>{this.store[e]=!0}))}),(s=>{r((()=>{this.errorJson(s),this.store[e]=s}))}))),t.run(this.promise)};subquery=(s,e)=>{const{id:r,hash:i}=e,o=this.subqueries.get(r);if(!o)return void this.subqueries.set(r,{hash:i});if(o?.hash===i)return t.run(o?.promise);const a=s();return this.subqueries.set(r,{hash:i,promise:a}),t.run(a)};static run=s=>{if(s){switch(s.status){case"fulfilled":return s.value;case"pending":throw s;case"rejected":throw s.reason;default:s.status="pending",s.then((e=>{s.status="fulfilled",s.value=e}),(e=>{s.status="rejected",s.reason=e}))}throw s}}}export{t as default};
2
2
  //# sourceMappingURL=suspense-query.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"suspense-query.js","sources":["../src/suspense-query.ts"],"sourcesContent":["import { extendObservable, observable, runInAction } from 'mobx';\nimport type { TInitStore } from './types';\n\nexport interface IPromise<TReturn> extends Promise<TReturn> {\n status?: 'fulfilled' | 'pending' | 'rejected';\n value?: TReturn;\n reason?: any;\n}\n\ninterface ISuspenseQueryParams {\n fieldName?: string; // field name in target store for save suspense state\n errorFields?: string[];\n}\n\n/**\n * Run request and cache promise\n * Sync suspense status between server and client\n */\nclass SuspenseQuery {\n /**\n * @protected\n */\n protected promise: Promise<any> | undefined;\n\n /**\n * Target store\n */\n protected readonly store: TInitStore;\n\n /**\n * @protected\n */\n protected readonly params: Required<ISuspenseQueryParams>;\n\n /**\n * @constructor\n */\n constructor(\n store: TInitStore,\n { fieldName = 'sR', errorFields = ['name', 'message'] }: ISuspenseQueryParams = {},\n ) {\n this.store = store;\n this.params = { fieldName, errorFields };\n\n const defaultInit = store.init?.bind(store);\n\n store.init = () => {\n this.isComplete(); // throw error immediately from server side if exist\n defaultInit?.();\n };\n\n extendObservable(\n store,\n { [fieldName]: false },\n {\n [fieldName]: observable,\n },\n );\n }\n\n /**\n * Error to json\n */\n protected errorJson(e: any): void {\n e.toJSON = () =>\n this.params.errorFields.reduce(\n (res, name) => ({\n ...res,\n [name]: e?.[name],\n }),\n {},\n );\n }\n\n /**\n * Assign custom error fields to error\n */\n protected jsonToError(e: Error, values: Record<string, any>): Error {\n this.params.errorFields.forEach((name) => {\n e[name] = values?.[name];\n });\n\n return e;\n }\n\n /**\n * Detect if suspense is restored from server side:\n * - throw error if exist\n * - skip run suspense if already completed\n */\n protected isComplete(): boolean {\n const value = this.store[this.params.fieldName];\n const valueType = typeof value;\n\n // pass error to error boundary\n if (valueType !== 'boolean') {\n throw this.jsonToError(\n new Error((value?.message ?? value?.name) as string),\n value as Record<string, any>,\n );\n }\n\n return value === true;\n }\n\n /**\n * Run request\n * Save request resolve status\n */\n public query = <TReturn>(promise: () => Promise<TReturn>): TReturn | undefined => {\n const { fieldName } = this.params;\n\n if (this.isComplete()) {\n return;\n }\n\n if (!this.promise) {\n this.promise = promise();\n\n this.promise.then(\n () => {\n runInAction(() => {\n this.store[fieldName] = true;\n });\n },\n (e) => {\n runInAction(() => {\n this.errorJson(e);\n\n this.store[fieldName] = e;\n });\n },\n );\n }\n\n return SuspenseQuery.run<TReturn>(this.promise);\n };\n\n /**\n * Change status of promise.\n * Throw promise to react suspense\n */\n public static run = <TReturn>(promise: IPromise<TReturn> | undefined): TReturn | undefined => {\n if (!promise) {\n return;\n }\n\n switch (promise.status) {\n case 'fulfilled':\n return promise.value;\n\n case 'pending':\n throw promise;\n\n case 'rejected':\n throw promise.reason;\n\n default:\n promise.status = 'pending';\n\n promise.then(\n (result) => {\n promise.status = 'fulfilled';\n promise.value = result;\n },\n (reason) => {\n promise.status = 'rejected';\n promise.reason = reason;\n },\n );\n }\n\n throw promise;\n };\n}\n\nexport default SuspenseQuery;\n"],"names":["SuspenseQuery","promise","store","params","constructor","fieldName","errorFields","this","defaultInit","init","bind","isComplete","extendObservable","observable","errorJson","e","toJSON","reduce","res","name","jsonToError","values","forEach","value","Error","message","query","then","runInAction","run","static","status","reason","result"],"mappings":"yEAkBA,MAAMA,EAIMC,QAKSC,MAKAC,OAKnBC,YACEF,GACAG,UAAEA,EAAY,KAAIC,YAAEA,EAAc,CAAC,OAAQ,YAAqC,IAEhFC,KAAKL,MAAQA,EACbK,KAAKJ,OAAS,CAAEE,YAAWC,eAE3B,MAAME,EAAcN,EAAMO,MAAMC,KAAKR,GAErCA,EAAMO,KAAO,KACXF,KAAKI,aACLH,KAAe,EAGjBI,EACEV,EACA,CAAEG,CAACA,IAAY,GACf,CACEA,CAACA,GAAYQ,GAGlB,CAKSC,UAAUC,GAClBA,EAAEC,OAAS,IACTT,KAAKJ,OAAOG,YAAYW,QACtB,CAACC,EAAKC,KAAU,IACXD,EACHC,CAACA,GAAOJ,IAAII,MAEd,CAAE,EAEP,CAKSC,YAAYL,EAAUM,GAK9B,OAJAd,KAAKJ,OAAOG,YAAYgB,SAASH,IAC/BJ,EAAEI,GAAQE,IAASF,EAAK,IAGnBJ,CACR,CAOSJ,aACR,MAAMY,EAAQhB,KAAKL,MAAMK,KAAKJ,OAAOE,WAIrC,GAAkB,mBAHOkB,EAIvB,MAAMhB,KAAKa,YACT,IAAII,MAAOD,GAAOE,SAAWF,GAAOJ,MACpCI,GAIJ,OAAiB,IAAVA,CACR,CAMMG,MAAkBzB,IACvB,MAAMI,UAAEA,GAAcE,KAAKJ,OAE3B,IAAII,KAAKI,aAuBT,OAnBKJ,KAAKN,UACRM,KAAKN,QAAUA,IAEfM,KAAKN,QAAQ0B,MACX,KACEC,GAAY,KACVrB,KAAKL,MAAMG,IAAa,CAAI,GAC5B,IAEHU,IACCa,GAAY,KACVrB,KAAKO,UAAUC,GAEfR,KAAKL,MAAMG,GAAaU,CAAC,GACzB,KAKDf,EAAc6B,IAAatB,KAAKN,QAAQ,EAO1C6B,WAAuB7B,IAC5B,GAAKA,EAAL,CAIA,OAAQA,EAAQ8B,QACd,IAAK,YACH,OAAO9B,EAAQsB,MAEjB,IAAK,UACH,MAAMtB,EAER,IAAK,WACH,MAAMA,EAAQ+B,OAEhB,QACE/B,EAAQ8B,OAAS,UAEjB9B,EAAQ0B,MACLM,IACChC,EAAQ8B,OAAS,YACjB9B,EAAQsB,MAAQU,CAAM,IAEvBD,IACC/B,EAAQ8B,OAAS,WACjB9B,EAAQ+B,OAASA,CAAM,IAK/B,MAAM/B,CA3BL,CA2BY"}
1
+ {"version":3,"file":"suspense-query.js","sources":["../src/suspense-query.ts"],"sourcesContent":["import { extendObservable, observable, runInAction } from 'mobx';\nimport type { TInitStore } from './types';\n\nexport interface IPromise<TReturn> extends Promise<TReturn> {\n status?: 'fulfilled' | 'pending' | 'rejected';\n value?: TReturn;\n reason?: any;\n}\n\ninterface ISuspenseQueryParams {\n fieldName?: string; // field name in target store for save suspense state\n errorFields?: string[];\n}\n\ninterface ISuspenseSubqueryOptions {\n id: string;\n hash: unknown;\n}\n\n/**\n * Run request and cache promise\n * Sync suspense status between server and client\n */\nclass SuspenseQuery {\n /**\n * @protected\n */\n protected promise: Promise<any> | undefined;\n\n /**\n * Subqueries info\n */\n protected subqueries: Map<string, { hash: unknown; promise?: IPromise<any> }> = new Map();\n\n /**\n * Target store\n */\n protected readonly store: TInitStore;\n\n /**\n * @protected\n */\n protected readonly params: Required<ISuspenseQueryParams>;\n\n /**\n * @constructor\n */\n constructor(\n store: TInitStore,\n { fieldName = 'sR', errorFields = ['name', 'message'] }: ISuspenseQueryParams = {},\n ) {\n this.store = store;\n this.params = { fieldName, errorFields };\n\n const defaultInit = store.init?.bind(store);\n\n store.init = () => {\n this.isComplete(); // throw error immediately from server side if exist\n defaultInit?.();\n };\n\n extendObservable(\n store,\n { [fieldName]: false },\n {\n [fieldName]: observable,\n },\n );\n }\n\n /**\n * Error to json\n */\n protected errorJson(e: any): void {\n e.toJSON = () =>\n this.params.errorFields.reduce(\n (res, name) => ({\n ...res,\n [name]: e?.[name],\n }),\n {},\n );\n }\n\n /**\n * Assign custom error fields to error\n */\n protected jsonToError(e: Error, values: Record<string, any>): Error {\n this.params.errorFields.forEach((name) => {\n e[name] = values?.[name];\n });\n\n return e;\n }\n\n /**\n * Detect if suspense is restored from server side:\n * - throw error if exist\n * - skip run suspense if already completed\n */\n protected isComplete(): boolean {\n const value = this.store[this.params.fieldName];\n const valueType = typeof value;\n\n // pass error to error boundary\n if (valueType !== 'boolean') {\n throw this.jsonToError(\n new Error((value?.message ?? value?.name) as string),\n value as Record<string, any>,\n );\n }\n\n return value === true;\n }\n\n /**\n * Run request\n * Save request resolve status\n */\n public query = <TReturn>(promise: () => Promise<TReturn>): TReturn | undefined => {\n const { fieldName } = this.params;\n\n if (this.isComplete()) {\n return;\n }\n\n if (!this.promise) {\n this.promise = promise();\n\n this.promise.then(\n () => {\n runInAction(() => {\n this.store[fieldName] = true;\n });\n },\n (e) => {\n runInAction(() => {\n this.errorJson(e);\n\n this.store[fieldName] = e;\n });\n },\n );\n }\n\n return SuspenseQuery.run<TReturn>(this.promise);\n };\n\n /**\n * Run subquery\n * Re-fetch data from query by hash changes in children components\n * NOTE: only client side\n */\n public subquery = <TReturn>(\n promise: () => Promise<TReturn>,\n options: ISuspenseSubqueryOptions,\n ): TReturn | undefined => {\n const { id, hash } = options;\n const subquery = this.subqueries.get(id);\n\n // skip first run\n if (!subquery) {\n this.subqueries.set(id, { hash });\n\n return undefined;\n }\n\n if (subquery?.hash === hash) {\n return SuspenseQuery.run<TReturn>(subquery?.promise);\n }\n\n const newQuery = promise();\n\n this.subqueries.set(id, { hash, promise: newQuery });\n\n return SuspenseQuery.run<TReturn>(newQuery);\n };\n\n /**\n * Change status of promise.\n * Throw promise to react suspense\n */\n public static run = <TReturn>(promise: IPromise<TReturn> | undefined): TReturn | undefined => {\n if (!promise) {\n return;\n }\n\n switch (promise.status) {\n case 'fulfilled':\n return promise.value;\n\n case 'pending':\n throw promise;\n\n case 'rejected':\n throw promise.reason;\n\n default:\n promise.status = 'pending';\n\n promise.then(\n (result) => {\n promise.status = 'fulfilled';\n promise.value = result;\n },\n (reason) => {\n promise.status = 'rejected';\n promise.reason = reason;\n },\n );\n }\n\n throw promise;\n };\n}\n\nexport default SuspenseQuery;\n"],"names":["SuspenseQuery","promise","subqueries","Map","store","params","constructor","fieldName","errorFields","this","defaultInit","init","bind","isComplete","extendObservable","observable","errorJson","e","toJSON","reduce","res","name","jsonToError","values","forEach","value","Error","message","query","then","runInAction","run","subquery","options","id","hash","get","set","newQuery","static","status","reason","result"],"mappings":"yEAuBA,MAAMA,EAIMC,QAKAC,WAAsE,IAAIC,IAKjEC,MAKAC,OAKnBC,YACEF,GACAG,UAAEA,EAAY,KAAIC,YAAEA,EAAc,CAAC,OAAQ,YAAqC,IAEhFC,KAAKL,MAAQA,EACbK,KAAKJ,OAAS,CAAEE,YAAWC,eAE3B,MAAME,EAAcN,EAAMO,MAAMC,KAAKR,GAErCA,EAAMO,KAAO,KACXF,KAAKI,aACLH,KAAe,EAGjBI,EACEV,EACA,CAAEG,CAACA,IAAY,GACf,CACEA,CAACA,GAAYQ,GAGlB,CAKSC,UAAUC,GAClBA,EAAEC,OAAS,IACTT,KAAKJ,OAAOG,YAAYW,QACtB,CAACC,EAAKC,KAAU,IACXD,EACHC,CAACA,GAAOJ,IAAII,MAEd,CAAE,EAEP,CAKSC,YAAYL,EAAUM,GAK9B,OAJAd,KAAKJ,OAAOG,YAAYgB,SAASH,IAC/BJ,EAAEI,GAAQE,IAASF,EAAK,IAGnBJ,CACR,CAOSJ,aACR,MAAMY,EAAQhB,KAAKL,MAAMK,KAAKJ,OAAOE,WAIrC,GAAkB,mBAHOkB,EAIvB,MAAMhB,KAAKa,YACT,IAAII,MAAOD,GAAOE,SAAWF,GAAOJ,MACpCI,GAIJ,OAAiB,IAAVA,CACR,CAMMG,MAAkB3B,IACvB,MAAMM,UAAEA,GAAcE,KAAKJ,OAE3B,IAAII,KAAKI,aAuBT,OAnBKJ,KAAKR,UACRQ,KAAKR,QAAUA,IAEfQ,KAAKR,QAAQ4B,MACX,KACEC,GAAY,KACVrB,KAAKL,MAAMG,IAAa,CAAI,GAC5B,IAEHU,IACCa,GAAY,KACVrB,KAAKO,UAAUC,GAEfR,KAAKL,MAAMG,GAAaU,CAAC,GACzB,KAKDjB,EAAc+B,IAAatB,KAAKR,QAAQ,EAQ1C+B,SAAW,CAChB/B,EACAgC,KAEA,MAAMC,GAAEA,EAAEC,KAAEA,GAASF,EACfD,EAAWvB,KAAKP,WAAWkC,IAAIF,GAGrC,IAAKF,EAGH,YAFAvB,KAAKP,WAAWmC,IAAIH,EAAI,CAAEC,SAK5B,GAAIH,GAAUG,OAASA,EACrB,OAAOnC,EAAc+B,IAAaC,GAAU/B,SAG9C,MAAMqC,EAAWrC,IAIjB,OAFAQ,KAAKP,WAAWmC,IAAIH,EAAI,CAAEC,OAAMlC,QAASqC,IAElCtC,EAAc+B,IAAaO,EAAS,EAOtCC,WAAuBtC,IAC5B,GAAKA,EAAL,CAIA,OAAQA,EAAQuC,QACd,IAAK,YACH,OAAOvC,EAAQwB,MAEjB,IAAK,UACH,MAAMxB,EAER,IAAK,WACH,MAAMA,EAAQwC,OAEhB,QACExC,EAAQuC,OAAS,UAEjBvC,EAAQ4B,MACLa,IACCzC,EAAQuC,OAAS,YACjBvC,EAAQwB,MAAQiB,CAAM,IAEvBD,IACCxC,EAAQuC,OAAS,WACjBvC,EAAQwC,OAASA,CAAM,IAK/B,MAAMxC,CA3BL,CA2BY"}