@lomray/react-mobx-manager 3.2.0 → 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 +1 -1
- package/suspense-query.d.ts +17 -0
- package/suspense-query.js +1 -1
- package/suspense-query.js.map +1 -1
package/package.json
CHANGED
package/suspense-query.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/suspense-query.js.map
CHANGED
|
@@ -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":"
|
|
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"}
|