@jayalfredprufrock/mobx-toolbox 0.6.1 → 0.6.3
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/dialog.d.mts +6 -8
- package/dist/dialog.d.mts.map +1 -1
- package/dist/dialog.mjs +2 -0
- package/dist/dialog.mjs.map +1 -1
- package/dist/form.d.mts +6 -9
- package/dist/form.d.mts.map +1 -1
- package/dist/form.mjs.map +1 -1
- package/dist/{lazy-observable-DA4DIMbc.d.mts → lazy-observable-Bx58qmfb.d.mts} +1 -1
- package/dist/lazy-observable-Bx58qmfb.d.mts.map +1 -0
- package/dist/{lazy-observable-CdHXpUkk.mjs → lazy-observable-Ce5e1Izm.mjs} +1 -1
- package/dist/{lazy-observable-CdHXpUkk.mjs.map → lazy-observable-Ce5e1Izm.mjs.map} +1 -1
- package/dist/lazy-observable.d.mts +1 -1
- package/dist/lazy-observable.d.mts.map +1 -1
- package/dist/lazy-observable.mjs +1 -1
- package/dist/lazy-observable.mjs.map +1 -1
- package/dist/model.d.mts +1 -1
- package/dist/model.d.mts.map +1 -1
- package/dist/model.mjs +1 -1
- package/dist/model.mjs.map +1 -1
- package/dist/react-util.d.mts.map +1 -1
- package/dist/react-util.mjs.map +1 -1
- package/dist/router.d.mts +4 -5
- package/dist/router.d.mts.map +1 -1
- package/dist/router.mjs +4 -1
- package/dist/router.mjs.map +1 -1
- package/dist/util.d.mts.map +1 -1
- package/dist/util.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/lazy-observable-DA4DIMbc.d.mts.map +0 -1
package/dist/dialog.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import * as _$react from "react";
|
|
2
1
|
import { FC } from "react";
|
|
3
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
2
|
|
|
5
3
|
//#region src/dialog/dialog.types.d.ts
|
|
6
4
|
type DialogState = "opening" | "opened" | "closing" | "closed";
|
|
@@ -48,7 +46,7 @@ interface MobxDialogsProps {
|
|
|
48
46
|
}
|
|
49
47
|
declare const MobxDialogs: (({
|
|
50
48
|
store
|
|
51
|
-
}: MobxDialogsProps) =>
|
|
49
|
+
}: MobxDialogsProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
52
50
|
displayName: string;
|
|
53
51
|
};
|
|
54
52
|
interface MobxDialogProps {
|
|
@@ -56,18 +54,18 @@ interface MobxDialogProps {
|
|
|
56
54
|
}
|
|
57
55
|
declare const MobxDialog: (({
|
|
58
56
|
dialog
|
|
59
|
-
}: MobxDialogProps) =>
|
|
57
|
+
}: MobxDialogProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
60
58
|
displayName: string;
|
|
61
59
|
};
|
|
62
60
|
//#endregion
|
|
63
61
|
//#region src/dialog/dialog.context.d.ts
|
|
64
|
-
declare const dialogStoreContext:
|
|
62
|
+
declare const dialogStoreContext: import("react").Context<DialogStore | undefined>;
|
|
65
63
|
declare const useDialogStore: () => DialogStore;
|
|
66
|
-
declare const DialogStoreProvider:
|
|
67
|
-
declare const dialogContext:
|
|
64
|
+
declare const DialogStoreProvider: import("react").Provider<DialogStore | undefined>;
|
|
65
|
+
declare const dialogContext: import("react").Context<DialogModel | undefined>;
|
|
68
66
|
declare const useDialogContext: () => DialogModel;
|
|
69
67
|
declare const useDialogContextIfAvailable: () => DialogModel | undefined;
|
|
70
|
-
declare const DialogProvider:
|
|
68
|
+
declare const DialogProvider: import("react").Provider<DialogModel | undefined>;
|
|
71
69
|
//#endregion
|
|
72
70
|
//#region src/dialog/use-dialogs.d.ts
|
|
73
71
|
declare const useDialogs: (store?: DialogStore) => DialogStore;
|
package/dist/dialog.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.mts","names":[],"sources":["../src/dialog/dialog.types.ts","../src/dialog/dialog.store.ts","../src/dialog/dialog.model.ts","../src/dialog/components/dialogs.tsx","../src/dialog/dialog.context.ts","../src/dialog/use-dialogs.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dialog.d.mts","names":[],"sources":["../src/dialog/dialog.types.ts","../src/dialog/dialog.store.ts","../src/dialog/dialog.model.ts","../src/dialog/components/dialogs.tsx","../src/dialog/dialog.context.ts","../src/dialog/use-dialogs.ts"],"mappings":";;;KAEY,WAAA;AAAA,KAEP,YAAA,oBAAgC,CAAA,gBAAiB,IAAA,CAAK,CAAA,EAAG,CAAA,YAAa,CAAA,SAAU,CAAA;AAAA,KAEzE,YAAA,GAAe,EAAE;AAAA,KAEjB,uBAAA,WAAkC,YAAA,MAAkB,UAAA,CAAW,CAAA,QACzE,YAAA,CAAa,CAAA,mBAAoB,CAAA,EAAG,CAAA,MAAO,CAAA,EAAG,CAAA;AAAA,UAE/B,iBAAA;EACf,EAAA;EACA,SAAA,EAAW,YAAA;EACX,KAAA,GAAQ,KAAA,CAAM,cAAA,CAAe,YAAA;EAC7B,cAAA;EACA,YAAA,GAAe,WAAA;AAAA;;;cCZJ,WAAA;EACX,OAAA,EAAO,GAAA,SAAA,WAAA;EAAA,IAEH,WAAA,IAAe,WAAA;EAAA,IAMf,YAAA,IAAgB,WAAA;EAIpB,WAAA;EAIA,GAAA,CAAI,MAAA,EAAQ,iBAAA,GAAoB,WAAA;EAMhC,IAAA,WAAe,YAAA,KAAiB,IAAA,EAAM,uBAAA,CAAwB,CAAA,IAAK,WAAA;EAUnE,KAAA,CAAM,gBAAA;AAAA;;;cCjCK,WAAA;EAAA,SAWA,KAAA,EAAO,WAAA;EAAA,SACP,MAAA,EAAQ,iBAAA;EAAA,SAXV,EAAA;EAAA,SACA,SAAA,EAAW,YAAA;EAAA,SACX,KAAA,GAAQ,KAAA,CAAM,cAAA,CAAe,YAAA;EAEtC,QAAA;EACA,wBAAA,EAA0B,WAAA;EAE1B,KAAA,EAAQ,WAAA;EAER,WAAA,CACW,KAAA,EAAO,WAAA,EACP,MAAA,EAAQ,iBAAA;EAiBnB,KAAA,CAAM,gBAAA;EAIN,IAAA,CAAK,eAAA;EAIL,QAAA,CAAS,KAAA,EAAO,WAAA;AAAA;;;UCnCD,gBAAA;EACf,KAAA,GAAQ,WAAW;AAAA;AAAA,cAGR,WAAA;EAAW;AAAA,GAAwB,gBAAgB,iCAAA,GAAA,CAAA,OAAA;;;UAY/C,eAAA;EACf,MAAA,EAAQ,WAAW;AAAA;AAAA,cAGR,UAAA;EAAU;AAAA,GAAyB,eAAe,iCAAA,GAAA,CAAA,OAAA;;;;;cCtBlD,kBAAA,kBAAkB,OAAA,CAAA,WAAA;AAAA,cAClB,cAAA,QAAc,WAM1B;AAAA,cAEY,mBAAA,kBAAmB,QAAA,CAAA,WAAA;AAAA,cAEnB,aAAA,kBAAa,OAAA,CAAA,WAAA;AAAA,cACb,gBAAA,QAAgB,WAM5B;AAAA,cAEY,2BAAA,QAA2B,WAAA;AAAA,cAE3B,cAAA,kBAAc,QAAA,CAAA,WAAA;;;cCvBd,UAAA,GAAU,KAAA,GAAY,WAAA,KAAc,WAMhD"}
|
package/dist/dialog.mjs
CHANGED
package/dist/dialog.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.mjs","names":[],"sources":["../src/dialog/dialog.context.ts","../src/dialog/dialog.model.ts","../src/dialog/dialog.store.ts","../src/dialog/use-dialogs.ts","../src/dialog/components/dialogs.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DialogModel } from \"./dialog.model\";\nimport type { DialogStore } from \"./dialog.store\";\n\nexport const dialogStoreContext = createContext<DialogStore | undefined>(undefined);\nexport const useDialogStore = () => {\n const context = useContext(dialogStoreContext);\n if (!context) {\n throw new Error(\"Dialog store context not available. Are you using the <Dialogs /> component?\");\n }\n return context;\n};\n\nexport const DialogStoreProvider = dialogStoreContext.Provider;\n\nexport const dialogContext = createContext<DialogModel | undefined>(undefined);\nexport const useDialogContext = () => {\n const context = useContext(dialogContext);\n if (!context) {\n throw new Error(\"Dialog context not available. Are you using <DialogProvider /> ?\");\n }\n return context;\n};\n\nexport const useDialogContextIfAvailable = () => useContext(dialogContext);\n\nexport const DialogProvider = dialogContext.Provider;\n","import { makeAutoObservable } from \"mobx\";\nimport type { DialogStore } from \"./dialog.store\";\nimport type { AnyComponent, DialogModelConfig, DialogState } from \"./dialog.types\";\n\nexport class DialogModel {\n readonly id: string;\n readonly component: AnyComponent;\n readonly props?: React.ComponentProps<AnyComponent>;\n\n openedAt = 0;\n previouslyFocusedElement: HTMLElement | null = null;\n\n state!: DialogState;\n\n constructor(\n readonly store: DialogStore,\n readonly config: DialogModelConfig,\n ) {\n this.id = config.id ?? btoa(Math.random().toString()).substring(3, 12);\n this.component = config.component;\n this.props = config.props;\n\n this.setState(config.initialState ?? \"closed\");\n\n makeAutoObservable(this, {\n id: false,\n component: false,\n props: false,\n openedAt: false,\n previouslyFocusedElement: false,\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.setState(closeImmediately ? \"closed\" : \"closing\");\n }\n\n open(openImmediately?: boolean): void {\n this.setState(openImmediately ? \"opened\" : \"opening\");\n }\n\n setState(state: DialogState): void {\n if (this.state === state) return;\n\n this.state = state;\n if (state === \"opened\" || state === \"opening\") {\n this.openedAt = Date.now();\n if (state === \"opening\") {\n this.previouslyFocusedElement =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n }\n } else if (state === \"closed\") {\n this.previouslyFocusedElement?.focus();\n if (this.config.removeOnClosed) {\n this.store.dialogs.delete(this.id);\n }\n this.previouslyFocusedElement = null;\n }\n }\n}\n","import { makeAutoObservable } from \"mobx\";\nimport { DialogModel } from \"./dialog.model\";\nimport type { AnyComponent, DialogComponentAndProps, DialogModelConfig } from \"./dialog.types\";\n\nexport class DialogStore {\n dialogs = new Map<string, DialogModel>();\n\n get openDialogs(): DialogModel[] {\n return [...this.dialogs.values()]\n .filter((d) => d.state !== \"closed\")\n .sort((d1, d2) => d1.openedAt - d2.openedAt);\n }\n\n get activeDialog(): DialogModel | undefined {\n return this.openDialogs.at(-1);\n }\n\n constructor() {\n makeAutoObservable(this);\n }\n\n add(config: DialogModelConfig): DialogModel {\n const dialogModel = new DialogModel(this, config);\n this.dialogs.set(dialogModel.id, dialogModel);\n return dialogModel;\n }\n\n open<C extends AnyComponent>(...args: DialogComponentAndProps<C>): DialogModel {\n const [component, props] = args;\n return this.add({\n component,\n props,\n removeOnClosed: true,\n initialState: \"opening\",\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.activeDialog?.close(closeImmediately);\n }\n}\n","import { useRef } from \"react\";\nimport { DialogStore } from \"./dialog.store\";\n\nexport const useDialogs = (store?: DialogStore): DialogStore => {\n const storeRef = useRef<DialogStore | undefined>(store);\n if (!storeRef.current) {\n storeRef.current = new DialogStore();\n }\n return storeRef.current;\n};\n","import { observer } from \"mobx-react-lite\";\nimport { DialogProvider, DialogStoreProvider } from \"../dialog.context\";\nimport type { DialogModel } from \"../dialog.model\";\nimport type { DialogStore } from \"../dialog.store\";\nimport { useDialogs } from \"../use-dialogs\";\n\nexport interface MobxDialogsProps {\n store?: DialogStore;\n}\n\nexport const MobxDialogs = observer(({ store }: MobxDialogsProps) => {\n const dialogStore = useDialogs(store);\n\n return (\n <DialogStoreProvider value={dialogStore}>\n {dialogStore.openDialogs.map((dialog) => {\n return <MobxDialog key={dialog.id} dialog={dialog} />;\n })}\n </DialogStoreProvider>\n );\n});\n\nexport interface MobxDialogProps {\n dialog: DialogModel;\n}\n\nexport const MobxDialog = observer(({ dialog }: MobxDialogProps) => {\n const { component: DialogComponent, props: dialogProps } = dialog;\n return (\n <DialogProvider value={dialog}>\n <DialogComponent {...dialogProps} />\n </DialogProvider>\n );\n});\n"],"mappings":";;;;;;AAIA,MAAa,qBAAqB,cAAuC,
|
|
1
|
+
{"version":3,"file":"dialog.mjs","names":[],"sources":["../src/dialog/dialog.context.ts","../src/dialog/dialog.model.ts","../src/dialog/dialog.store.ts","../src/dialog/use-dialogs.ts","../src/dialog/components/dialogs.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DialogModel } from \"./dialog.model\";\nimport type { DialogStore } from \"./dialog.store\";\n\nexport const dialogStoreContext = createContext<DialogStore | undefined>(undefined);\nexport const useDialogStore = () => {\n const context = useContext(dialogStoreContext);\n if (!context) {\n throw new Error(\"Dialog store context not available. Are you using the <Dialogs /> component?\");\n }\n return context;\n};\n\nexport const DialogStoreProvider = dialogStoreContext.Provider;\n\nexport const dialogContext = createContext<DialogModel | undefined>(undefined);\nexport const useDialogContext = () => {\n const context = useContext(dialogContext);\n if (!context) {\n throw new Error(\"Dialog context not available. Are you using <DialogProvider /> ?\");\n }\n return context;\n};\n\nexport const useDialogContextIfAvailable = () => useContext(dialogContext);\n\nexport const DialogProvider = dialogContext.Provider;\n","import { makeAutoObservable } from \"mobx\";\nimport type { DialogStore } from \"./dialog.store\";\nimport type { AnyComponent, DialogModelConfig, DialogState } from \"./dialog.types\";\n\nexport class DialogModel {\n readonly id: string;\n readonly component: AnyComponent;\n readonly props?: React.ComponentProps<AnyComponent>;\n\n openedAt = 0;\n previouslyFocusedElement: HTMLElement | null = null;\n\n state!: DialogState;\n\n constructor(\n readonly store: DialogStore,\n readonly config: DialogModelConfig,\n ) {\n this.id = config.id ?? btoa(Math.random().toString()).substring(3, 12);\n this.component = config.component;\n this.props = config.props;\n\n this.setState(config.initialState ?? \"closed\");\n\n makeAutoObservable(this, {\n id: false,\n component: false,\n props: false,\n openedAt: false,\n previouslyFocusedElement: false,\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.setState(closeImmediately ? \"closed\" : \"closing\");\n }\n\n open(openImmediately?: boolean): void {\n this.setState(openImmediately ? \"opened\" : \"opening\");\n }\n\n setState(state: DialogState): void {\n if (this.state === state) return;\n\n this.state = state;\n if (state === \"opened\" || state === \"opening\") {\n this.openedAt = Date.now();\n if (state === \"opening\") {\n this.previouslyFocusedElement =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n }\n } else if (state === \"closed\") {\n this.previouslyFocusedElement?.focus();\n if (this.config.removeOnClosed) {\n this.store.dialogs.delete(this.id);\n }\n this.previouslyFocusedElement = null;\n }\n }\n}\n","import { makeAutoObservable } from \"mobx\";\nimport { DialogModel } from \"./dialog.model\";\nimport type { AnyComponent, DialogComponentAndProps, DialogModelConfig } from \"./dialog.types\";\n\nexport class DialogStore {\n dialogs = new Map<string, DialogModel>();\n\n get openDialogs(): DialogModel[] {\n return [...this.dialogs.values()]\n .filter((d) => d.state !== \"closed\")\n .sort((d1, d2) => d1.openedAt - d2.openedAt);\n }\n\n get activeDialog(): DialogModel | undefined {\n return this.openDialogs.at(-1);\n }\n\n constructor() {\n makeAutoObservable(this);\n }\n\n add(config: DialogModelConfig): DialogModel {\n const dialogModel = new DialogModel(this, config);\n this.dialogs.set(dialogModel.id, dialogModel);\n return dialogModel;\n }\n\n open<C extends AnyComponent>(...args: DialogComponentAndProps<C>): DialogModel {\n const [component, props] = args;\n return this.add({\n component,\n props,\n removeOnClosed: true,\n initialState: \"opening\",\n });\n }\n\n close(closeImmediately?: boolean): void {\n this.activeDialog?.close(closeImmediately);\n }\n}\n","import { useRef } from \"react\";\nimport { DialogStore } from \"./dialog.store\";\n\nexport const useDialogs = (store?: DialogStore): DialogStore => {\n const storeRef = useRef<DialogStore | undefined>(store);\n if (!storeRef.current) {\n storeRef.current = new DialogStore();\n }\n return storeRef.current;\n};\n","import { observer } from \"mobx-react-lite\";\nimport { DialogProvider, DialogStoreProvider } from \"../dialog.context\";\nimport type { DialogModel } from \"../dialog.model\";\nimport type { DialogStore } from \"../dialog.store\";\nimport { useDialogs } from \"../use-dialogs\";\n\nexport interface MobxDialogsProps {\n store?: DialogStore;\n}\n\nexport const MobxDialogs = observer(({ store }: MobxDialogsProps) => {\n const dialogStore = useDialogs(store);\n\n return (\n <DialogStoreProvider value={dialogStore}>\n {dialogStore.openDialogs.map((dialog) => {\n return <MobxDialog key={dialog.id} dialog={dialog} />;\n })}\n </DialogStoreProvider>\n );\n});\n\nexport interface MobxDialogProps {\n dialog: DialogModel;\n}\n\nexport const MobxDialog = observer(({ dialog }: MobxDialogProps) => {\n const { component: DialogComponent, props: dialogProps } = dialog;\n return (\n <DialogProvider value={dialog}>\n <DialogComponent {...dialogProps} />\n </DialogProvider>\n );\n});\n"],"mappings":"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"}
|
package/dist/form.d.mts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import * as _$react from "react";
|
|
2
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
1
|
import { Validator } from "typebox/schema";
|
|
4
|
-
import * as _$typebox from "typebox";
|
|
5
2
|
import { Static, TLiteral, TObject, TSchema, TUnion } from "typebox";
|
|
6
3
|
|
|
7
4
|
//#region src/form/form-field.model.d.ts
|
|
@@ -86,7 +83,7 @@ declare class FormModel<T extends FormSchema = TObject> {
|
|
|
86
83
|
interface MobxFormProps extends Omit<React.HTMLProps<HTMLFormElement>, "form" | "action" | "method"> {
|
|
87
84
|
form: FormModel<any>;
|
|
88
85
|
}
|
|
89
|
-
declare const MobxForm:
|
|
86
|
+
declare const MobxForm: import("react").ForwardRefExoticComponent<Omit<MobxFormProps, "ref"> & import("react").RefAttributes<unknown>>;
|
|
90
87
|
//#endregion
|
|
91
88
|
//#region src/form/components/form-when.d.ts
|
|
92
89
|
interface FormWhenProps<T extends TUnion, D extends DiscriminatorKeys<T>, V extends string> {
|
|
@@ -108,13 +105,13 @@ declare function FormWhen<T extends TUnion, D extends DiscriminatorKeys<T>, V ex
|
|
|
108
105
|
field,
|
|
109
106
|
value,
|
|
110
107
|
children
|
|
111
|
-
}: FormWhenProps<T, D, V>):
|
|
108
|
+
}: FormWhenProps<T, D, V>): import("react/jsx-runtime").JSX.Element;
|
|
112
109
|
//#endregion
|
|
113
110
|
//#region src/form/form.context.d.ts
|
|
114
|
-
declare const formContext:
|
|
115
|
-
declare const useFormContext: () => FormModel<
|
|
116
|
-
declare const FormProvider:
|
|
117
|
-
declare const useFormContextIfAvailable: () => FormModel<
|
|
111
|
+
declare const formContext: import("react").Context<FormModel<import("typebox").TObject<import("typebox").TProperties>> | undefined>;
|
|
112
|
+
declare const useFormContext: () => FormModel<import("typebox").TObject<import("typebox").TProperties>>;
|
|
113
|
+
declare const FormProvider: import("react").Provider<FormModel<import("typebox").TObject<import("typebox").TProperties>> | undefined>;
|
|
114
|
+
declare const useFormContextIfAvailable: () => FormModel<import("typebox").TObject<import("typebox").TProperties>> | undefined;
|
|
118
115
|
//#endregion
|
|
119
116
|
//#region src/form/use-form.d.ts
|
|
120
117
|
declare const useForm: <T extends FormSchema = TObject>(schema: T, config: FormConfig<T>) => FormModel<T>;
|
package/dist/form.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.mts","names":[],"sources":["../src/form/form-field.model.ts","../src/form/form.types.ts","../src/form/form.model.ts","../src/form/components/form.tsx","../src/form/components/form-when.tsx","../src/form/form.context.ts","../src/form/use-form.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"form.d.mts","names":[],"sources":["../src/form/form-field.model.ts","../src/form/form.types.ts","../src/form/form.model.ts","../src/form/components/form.tsx","../src/form/components/form-when.tsx","../src/form/form.context.ts","../src/form/use-form.ts"],"mappings":"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"}
|
package/dist/form.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.mjs","names":[],"sources":["../src/form/form.context.ts","../src/form/components/form.tsx","../src/form/components/form-when.tsx","../src/form/form-field.model.ts","../src/form/form.model.ts","../src/form/use-form.ts"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FormModel } from \"./form.model\";\n\nexport const formContext = createContext<FormModel | undefined>(undefined);\nexport const useFormContext = () => {\n const context = useContext(formContext);\n if (!context) {\n throw new Error(\n \"Form context not available. Make sure you are within the <Form> component or providing the form context manually.\",\n );\n }\n return context;\n};\n\nexport const FormProvider = formContext.Provider;\n\nexport const useFormContextIfAvailable = () => useContext(formContext);\n","import { forwardRef } from \"react\";\nimport { FormProvider } from \"../form.context\";\nimport type { FormModel } from \"../form.model\";\n\nexport interface MobxFormProps extends Omit<\n React.HTMLProps<HTMLFormElement>,\n \"form\" | \"action\" | \"method\"\n> {\n form: FormModel<any>;\n}\n\nexport const MobxForm = forwardRef(function MobxForm(\n { form, children, ...formProps }: MobxFormProps,\n ref,\n) {\n return (\n <FormProvider value={form}>\n <form noValidate {...formProps} ref={ref} {...form.props()}>\n {children}\n </form>\n </FormProvider>\n );\n});\n","import type { TUnion } from \"typebox\";\nimport { Observer } from \"mobx-react-lite\";\nimport type { FormModel } from \"../form.model\";\nimport type { FormFieldModel } from \"../form-field.model\";\nimport type {\n DiscriminatorKeys,\n DiscriminatorValue,\n FormFields,\n MatchVariant,\n} from \"../form.types\";\n\nexport interface FormWhenProps<T extends TUnion, D extends DiscriminatorKeys<T>, V extends string> {\n form: FormModel<T>;\n /** The discriminator field name. */\n field: D;\n /** Render the children only while the discriminator field equals this value. */\n value: V & DiscriminatorValue<T, D>;\n /** Receives the matching variant's fields. The form itself is already in scope. */\n children: (fields: FormFields<MatchVariant<T, D, V>>) => React.ReactNode;\n}\n\n/**\n * Renders its children only when the form's discriminator field currently holds\n * `value`, passing the fields narrowed to that variant. Stack one per variant to\n * lay out a discriminated-union form without manual conditionals or casts.\n */\nexport function FormWhen<T extends TUnion, D extends DiscriminatorKeys<T>, V extends string>({\n form,\n field,\n value,\n children,\n}: FormWhenProps<T, D, V>) {\n return (\n <Observer>\n {() => {\n const fields = form.rawFields as Record<string, FormFieldModel>;\n if (fields[field as string]?.value !== value) return null;\n return <>{children(fields as unknown as FormFields<MatchVariant<T, D, V>>)}</>;\n }}\n </Observer>\n );\n}\n","import Schema, { Validator } from \"typebox/schema\";\nimport * as Value from \"typebox/value\";\nimport * as T from \"typebox\";\nimport { makeAutoObservable, toJS } from \"mobx\";\nimport type { FormFieldConfig } from \"./form.types\";\n\n// TODO: should infer required prop\n// TODO: should also allow for \"id\", defaulting to name\n// TODO: probably shouldn't assume value is of correct type, maybe unknown?\n\nexport class FormFieldModel<T extends T.TSchema = T.TSchema> {\n readonly name: string;\n readonly schema: T;\n readonly config: FormFieldConfig<T>;\n readonly validator: Validator<T>;\n\n value: T.Static<T> | undefined;\n touched = false;\n\n get valid(): boolean {\n if (this.value === undefined && T.IsOptional(this.schema)) return true;\n return this.validator.Check(this.value);\n }\n\n get errorMessage(): string {\n if (this.valid || !this.touched) return \"\";\n // TODO: revisit this, now that .Errors returns success/fail\n const [_, errors] = this.validator.Errors(this.value);\n const error = errors.at(0);\n\n if (!error) return \"\";\n\n if (!T.IsOptional(this.schema) && (this.value === undefined || this.value === \"\")) {\n return \"This field is required.\";\n }\n\n if (\"errorMessage\" in this.schema) {\n if (typeof this.schema.errorMessage === \"string\") {\n return this.schema.errorMessage;\n } else if (typeof this.schema.errorMessage === \"function\") {\n return this.schema.errorMessage(this.value, this.schema);\n }\n }\n\n if (T.IsString(this.schema) && \"format\" in this.schema) {\n return `Please enter a valid ${String(this.schema.format)}`;\n }\n\n return error.message;\n }\n\n constructor(config: FormFieldConfig<T>) {\n this.config = config;\n this.name = config.name;\n this.schema = config.schema;\n this.validator = Schema.Compile(this.schema);\n this.value = Value.Convert(this.schema, config.initialValue) as T.Static<T>;\n\n makeAutoObservable(this, {\n name: false,\n schema: false,\n config: false,\n validator: false,\n });\n }\n\n setValue(value?: T.Static<T>) {\n // TODO: does this still make sense? If value is invalid,\n // then type will be wrong...revisit this\n this.value = Value.Convert(this.schema, value) as T.Static<T>;\n }\n\n setTouched(touched: boolean) {\n this.touched = touched;\n }\n\n reset() {\n this.setTouched(false);\n this.setValue(this.config.initialValue);\n }\n\n // TODO: this any type is dangerous, need to figure out a good way\n // to make this work OTTB for most form controls, while allowing\n // some kind of escape hatch for special cases\n props(): any {\n return {\n name: this.name,\n onChange: (v?: T.Static<T>) => this.setValue(v),\n value: this.value,\n onBlur: () => {\n this.setTouched(true);\n },\n };\n }\n\n toJSON(): T.Static<T> | undefined {\n return toJS(this.value);\n }\n}\n","import * as Format from \"typebox/format\";\nimport * as Value from \"typebox/value\";\nimport Schema, { type Validator } from \"typebox/schema\";\nimport { IsUnion, Union, type Static, type TObject, type TSchema } from \"typebox\";\nimport { makeAutoObservable } from \"mobx\";\nimport type { FormConfig, FormFields, FormSchema, RawFormFields } from \"./form.types\";\nimport { FormFieldModel } from \"./form-field.model\";\n\n// should these be here?\nFormat.Set(\"password\", () => true);\nFormat.Set(\"phone\", () => true);\n\n// Flatten a schema's fields into a single property map. For a discriminated\n// union this merges the properties of every variant, unioning the schemas of\n// any field that appears in more than one variant (which naturally turns the\n// discriminator into a union of its literals).\nfunction resolveProperties(schema: FormSchema): Record<string, TSchema> {\n if (!IsUnion(schema)) return schema.properties;\n\n const groups: Record<string, TSchema[]> = {};\n for (const variant of schema.anyOf) {\n for (const [key, propSchema] of Object.entries((variant as TObject).properties)) {\n (groups[key] ??= []).push(propSchema as TSchema);\n }\n }\n\n const merged: Record<string, TSchema> = {};\n for (const [key, schemas] of Object.entries(groups)) {\n const distinct = [...new Map(schemas.map((s) => [JSON.stringify(s), s])).values()];\n merged[key] = distinct.length === 1 ? distinct[0]! : Union(distinct);\n }\n return merged;\n}\n\n// consider using enumerable to allow spreading of\n// field model instead of calling props() unless we need\n// to pass data to props...\n\n// TODO:\n// handleSubmit should catch a special MobxFormError\n// that can set field-level errors from an API response\n\nexport class FormModel<T extends FormSchema = TObject> {\n /** Shared fields only (for unions); every field for a plain object schema. */\n readonly fields: FormFields<T>;\n /** Every field across all variants — escape hatch for reaching variant fields. */\n readonly rawFields: RawFormFields<T>;\n readonly config: FormConfig<T>;\n readonly schema: T;\n readonly validator: Validator<T>;\n\n // TODO: maybe refactor into a \"state\" property?\n submitting = false;\n submitted = false;\n\n // TODO: what should this type be? should it be generic?\n submitError: any;\n\n constructor(schema: T, config: FormConfig<T>) {\n this.schema = schema;\n this.config = config;\n this.validator = Schema.Compile(schema);\n\n makeAutoObservable(this, {\n fields: false,\n rawFields: false,\n schema: false,\n config: false,\n validator: false,\n });\n\n const initialValues = config?.initialValues as Record<string, unknown> | undefined;\n const fields = Object.entries(resolveProperties(schema)).reduce(\n (fields, [fieldName, fieldSchema]) => {\n fields[fieldName] = new FormFieldModel({\n name: fieldName,\n schema: fieldSchema,\n initialValue: initialValues?.[fieldName],\n });\n return fields;\n },\n {} as Record<string, FormFieldModel<TSchema>>,\n );\n\n // `fields` and `rawFields` are the same object; the types differ so that a\n // union form only surfaces shared fields by default.\n this.rawFields = fields as unknown as RawFormFields<T>;\n this.fields = fields as unknown as FormFields<T>;\n }\n\n get valid(): boolean {\n // A union form can't be validated field-by-field — fields belonging to the\n // inactive variant would fail — so validate the assembled object instead.\n if (IsUnion(this.schema)) {\n return this.validator.Check(this.toJSON());\n }\n return Object.values(this.fields).every((field) => field.valid);\n }\n\n props(): any {\n return {\n onSubmit: (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n\n this.setSubmitError(undefined);\n\n if (!this.validate()) {\n return;\n }\n\n this.setSubmitting(true);\n this.config\n .handleSubmit(this.toJSON() as Static<T>)\n .then((resp) => {\n this.setSubmitted(true);\n return resp;\n })\n .catch((e) => {\n this.setSubmitError(e);\n })\n .finally(() => {\n this.setSubmitting(false);\n });\n },\n };\n }\n\n setSubmitError(error: any): void {\n this.submitError = error;\n }\n\n protected setSubmitting(submitting: boolean): void {\n this.submitting = submitting;\n }\n\n protected setSubmitted(submitted: boolean): void {\n this.submitted = submitted;\n }\n\n reset(): void {\n this.setSubmitError(undefined);\n for (const field of Object.values(this.fields)) {\n field.reset();\n }\n }\n\n validate(): boolean {\n for (const field of Object.values(this.fields)) {\n field.setTouched(true);\n }\n return this.valid;\n }\n\n toJSON(): Partial<Static<T>> {\n const data = Object.values(this.fields).reduce(\n (fields, field) => {\n fields[field.name] = field.toJSON();\n return fields;\n },\n {} as Record<string, unknown>,\n );\n\n // Drop fields belonging to the inactive variant so submitted data matches\n // the selected member of the union exactly.\n if (IsUnion(this.schema)) {\n return Value.Clean(this.schema, data) as Partial<Static<T>>;\n }\n return data as Partial<Static<T>>;\n }\n}\n","import type { TObject } from \"typebox\";\nimport { useRef } from \"react\";\nimport { FormModel } from \"./form.model\";\nimport type { FormConfig, FormSchema } from \"./form.types\";\n\nexport const useForm = <T extends FormSchema = TObject>(\n schema: T,\n config: FormConfig<T>,\n): FormModel<T> => {\n const formRef = useRef<FormModel<T>>(undefined);\n if (formRef.current) {\n Object.assign(formRef.current.config, config);\n } else {\n formRef.current = new FormModel<T>(schema, config);\n }\n\n return formRef.current;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"form.mjs","names":[],"sources":["../src/form/form.context.ts","../src/form/components/form.tsx","../src/form/components/form-when.tsx","../src/form/form-field.model.ts","../src/form/form.model.ts","../src/form/use-form.ts"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FormModel } from \"./form.model\";\n\nexport const formContext = createContext<FormModel | undefined>(undefined);\nexport const useFormContext = () => {\n const context = useContext(formContext);\n if (!context) {\n throw new Error(\n \"Form context not available. Make sure you are within the <Form> component or providing the form context manually.\",\n );\n }\n return context;\n};\n\nexport const FormProvider = formContext.Provider;\n\nexport const useFormContextIfAvailable = () => useContext(formContext);\n","import { forwardRef } from \"react\";\nimport { FormProvider } from \"../form.context\";\nimport type { FormModel } from \"../form.model\";\n\nexport interface MobxFormProps extends Omit<\n React.HTMLProps<HTMLFormElement>,\n \"form\" | \"action\" | \"method\"\n> {\n form: FormModel<any>;\n}\n\nexport const MobxForm = forwardRef(function MobxForm(\n { form, children, ...formProps }: MobxFormProps,\n ref,\n) {\n return (\n <FormProvider value={form}>\n <form noValidate {...formProps} ref={ref} {...form.props()}>\n {children}\n </form>\n </FormProvider>\n );\n});\n","import type { TUnion } from \"typebox\";\nimport { Observer } from \"mobx-react-lite\";\nimport type { FormModel } from \"../form.model\";\nimport type { FormFieldModel } from \"../form-field.model\";\nimport type {\n DiscriminatorKeys,\n DiscriminatorValue,\n FormFields,\n MatchVariant,\n} from \"../form.types\";\n\nexport interface FormWhenProps<T extends TUnion, D extends DiscriminatorKeys<T>, V extends string> {\n form: FormModel<T>;\n /** The discriminator field name. */\n field: D;\n /** Render the children only while the discriminator field equals this value. */\n value: V & DiscriminatorValue<T, D>;\n /** Receives the matching variant's fields. The form itself is already in scope. */\n children: (fields: FormFields<MatchVariant<T, D, V>>) => React.ReactNode;\n}\n\n/**\n * Renders its children only when the form's discriminator field currently holds\n * `value`, passing the fields narrowed to that variant. Stack one per variant to\n * lay out a discriminated-union form without manual conditionals or casts.\n */\nexport function FormWhen<T extends TUnion, D extends DiscriminatorKeys<T>, V extends string>({\n form,\n field,\n value,\n children,\n}: FormWhenProps<T, D, V>) {\n return (\n <Observer>\n {() => {\n const fields = form.rawFields as Record<string, FormFieldModel>;\n if (fields[field as string]?.value !== value) return null;\n return <>{children(fields as unknown as FormFields<MatchVariant<T, D, V>>)}</>;\n }}\n </Observer>\n );\n}\n","import Schema, { Validator } from \"typebox/schema\";\nimport * as Value from \"typebox/value\";\nimport * as T from \"typebox\";\nimport { makeAutoObservable, toJS } from \"mobx\";\nimport type { FormFieldConfig } from \"./form.types\";\n\n// TODO: should infer required prop\n// TODO: should also allow for \"id\", defaulting to name\n// TODO: probably shouldn't assume value is of correct type, maybe unknown?\n\nexport class FormFieldModel<T extends T.TSchema = T.TSchema> {\n readonly name: string;\n readonly schema: T;\n readonly config: FormFieldConfig<T>;\n readonly validator: Validator<T>;\n\n value: T.Static<T> | undefined;\n touched = false;\n\n get valid(): boolean {\n if (this.value === undefined && T.IsOptional(this.schema)) return true;\n return this.validator.Check(this.value);\n }\n\n get errorMessage(): string {\n if (this.valid || !this.touched) return \"\";\n // TODO: revisit this, now that .Errors returns success/fail\n const [_, errors] = this.validator.Errors(this.value);\n const error = errors.at(0);\n\n if (!error) return \"\";\n\n if (!T.IsOptional(this.schema) && (this.value === undefined || this.value === \"\")) {\n return \"This field is required.\";\n }\n\n if (\"errorMessage\" in this.schema) {\n if (typeof this.schema.errorMessage === \"string\") {\n return this.schema.errorMessage;\n } else if (typeof this.schema.errorMessage === \"function\") {\n return this.schema.errorMessage(this.value, this.schema);\n }\n }\n\n if (T.IsString(this.schema) && \"format\" in this.schema) {\n return `Please enter a valid ${String(this.schema.format)}`;\n }\n\n return error.message;\n }\n\n constructor(config: FormFieldConfig<T>) {\n this.config = config;\n this.name = config.name;\n this.schema = config.schema;\n this.validator = Schema.Compile(this.schema);\n this.value = Value.Convert(this.schema, config.initialValue) as T.Static<T>;\n\n makeAutoObservable(this, {\n name: false,\n schema: false,\n config: false,\n validator: false,\n });\n }\n\n setValue(value?: T.Static<T>) {\n // TODO: does this still make sense? If value is invalid,\n // then type will be wrong...revisit this\n this.value = Value.Convert(this.schema, value) as T.Static<T>;\n }\n\n setTouched(touched: boolean) {\n this.touched = touched;\n }\n\n reset() {\n this.setTouched(false);\n this.setValue(this.config.initialValue);\n }\n\n // TODO: this any type is dangerous, need to figure out a good way\n // to make this work OTTB for most form controls, while allowing\n // some kind of escape hatch for special cases\n props(): any {\n return {\n name: this.name,\n onChange: (v?: T.Static<T>) => this.setValue(v),\n value: this.value,\n onBlur: () => {\n this.setTouched(true);\n },\n };\n }\n\n toJSON(): T.Static<T> | undefined {\n return toJS(this.value);\n }\n}\n","import * as Format from \"typebox/format\";\nimport * as Value from \"typebox/value\";\nimport Schema, { type Validator } from \"typebox/schema\";\nimport { IsUnion, Union, type Static, type TObject, type TSchema } from \"typebox\";\nimport { makeAutoObservable } from \"mobx\";\nimport type { FormConfig, FormFields, FormSchema, RawFormFields } from \"./form.types\";\nimport { FormFieldModel } from \"./form-field.model\";\n\n// should these be here?\nFormat.Set(\"password\", () => true);\nFormat.Set(\"phone\", () => true);\n\n// Flatten a schema's fields into a single property map. For a discriminated\n// union this merges the properties of every variant, unioning the schemas of\n// any field that appears in more than one variant (which naturally turns the\n// discriminator into a union of its literals).\nfunction resolveProperties(schema: FormSchema): Record<string, TSchema> {\n if (!IsUnion(schema)) return schema.properties;\n\n const groups: Record<string, TSchema[]> = {};\n for (const variant of schema.anyOf) {\n for (const [key, propSchema] of Object.entries((variant as TObject).properties)) {\n (groups[key] ??= []).push(propSchema as TSchema);\n }\n }\n\n const merged: Record<string, TSchema> = {};\n for (const [key, schemas] of Object.entries(groups)) {\n const distinct = [...new Map(schemas.map((s) => [JSON.stringify(s), s])).values()];\n merged[key] = distinct.length === 1 ? distinct[0]! : Union(distinct);\n }\n return merged;\n}\n\n// consider using enumerable to allow spreading of\n// field model instead of calling props() unless we need\n// to pass data to props...\n\n// TODO:\n// handleSubmit should catch a special MobxFormError\n// that can set field-level errors from an API response\n\nexport class FormModel<T extends FormSchema = TObject> {\n /** Shared fields only (for unions); every field for a plain object schema. */\n readonly fields: FormFields<T>;\n /** Every field across all variants — escape hatch for reaching variant fields. */\n readonly rawFields: RawFormFields<T>;\n readonly config: FormConfig<T>;\n readonly schema: T;\n readonly validator: Validator<T>;\n\n // TODO: maybe refactor into a \"state\" property?\n submitting = false;\n submitted = false;\n\n // TODO: what should this type be? should it be generic?\n submitError: any;\n\n constructor(schema: T, config: FormConfig<T>) {\n this.schema = schema;\n this.config = config;\n this.validator = Schema.Compile(schema);\n\n makeAutoObservable(this, {\n fields: false,\n rawFields: false,\n schema: false,\n config: false,\n validator: false,\n });\n\n const initialValues = config?.initialValues as Record<string, unknown> | undefined;\n const fields = Object.entries(resolveProperties(schema)).reduce(\n (fields, [fieldName, fieldSchema]) => {\n fields[fieldName] = new FormFieldModel({\n name: fieldName,\n schema: fieldSchema,\n initialValue: initialValues?.[fieldName],\n });\n return fields;\n },\n {} as Record<string, FormFieldModel<TSchema>>,\n );\n\n // `fields` and `rawFields` are the same object; the types differ so that a\n // union form only surfaces shared fields by default.\n this.rawFields = fields as unknown as RawFormFields<T>;\n this.fields = fields as unknown as FormFields<T>;\n }\n\n get valid(): boolean {\n // A union form can't be validated field-by-field — fields belonging to the\n // inactive variant would fail — so validate the assembled object instead.\n if (IsUnion(this.schema)) {\n return this.validator.Check(this.toJSON());\n }\n return Object.values(this.fields).every((field) => field.valid);\n }\n\n props(): any {\n return {\n onSubmit: (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n\n this.setSubmitError(undefined);\n\n if (!this.validate()) {\n return;\n }\n\n this.setSubmitting(true);\n this.config\n .handleSubmit(this.toJSON() as Static<T>)\n .then((resp) => {\n this.setSubmitted(true);\n return resp;\n })\n .catch((e) => {\n this.setSubmitError(e);\n })\n .finally(() => {\n this.setSubmitting(false);\n });\n },\n };\n }\n\n setSubmitError(error: any): void {\n this.submitError = error;\n }\n\n protected setSubmitting(submitting: boolean): void {\n this.submitting = submitting;\n }\n\n protected setSubmitted(submitted: boolean): void {\n this.submitted = submitted;\n }\n\n reset(): void {\n this.setSubmitError(undefined);\n for (const field of Object.values(this.fields)) {\n field.reset();\n }\n }\n\n validate(): boolean {\n for (const field of Object.values(this.fields)) {\n field.setTouched(true);\n }\n return this.valid;\n }\n\n toJSON(): Partial<Static<T>> {\n const data = Object.values(this.fields).reduce(\n (fields, field) => {\n fields[field.name] = field.toJSON();\n return fields;\n },\n {} as Record<string, unknown>,\n );\n\n // Drop fields belonging to the inactive variant so submitted data matches\n // the selected member of the union exactly.\n if (IsUnion(this.schema)) {\n return Value.Clean(this.schema, data) as Partial<Static<T>>;\n }\n return data as Partial<Static<T>>;\n }\n}\n","import type { TObject } from \"typebox\";\nimport { useRef } from \"react\";\nimport { FormModel } from \"./form.model\";\nimport type { FormConfig, FormSchema } from \"./form.types\";\n\nexport const useForm = <T extends FormSchema = TObject>(\n schema: T,\n config: FormConfig<T>,\n): FormModel<T> => {\n const formRef = useRef<FormModel<T>>(undefined);\n if (formRef.current) {\n Object.assign(formRef.current.config, config);\n } else {\n formRef.current = new FormModel<T>(schema, config);\n }\n\n return formRef.current;\n};\n"],"mappings":"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"}
|
|
@@ -33,4 +33,4 @@ declare function lazyObservableArray<T>(fetch: () => Promise<T[]>, options?: Laz
|
|
|
33
33
|
type InferLazyObservable<O> = O extends LazyObservableArray<infer T> ? T[] : O extends LazyObservable<infer T> ? T : never;
|
|
34
34
|
//#endregion
|
|
35
35
|
export { LazyObservableOptions as a, lazyObservableArray as c, LazyObservableArrayOptions as i, LazyObservable as n, LazyObservableOptionsWithInitialValue as o, LazyObservableArray as r, lazyObservable as s, InferLazyObservable as t };
|
|
36
|
-
//# sourceMappingURL=lazy-observable-
|
|
36
|
+
//# sourceMappingURL=lazy-observable-Bx58qmfb.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-observable-Bx58qmfb.d.mts","names":[],"sources":["../src/lazy-observable/lazy-observable.ts"],"mappings":";;;UAUiB,cAAA,0BAAwC,CAAA;EACvD,KAAA,EAAO,aAAA;EACP,KAAA,IAAS,aAAA;EACT,SAAA,IAAa,OAAA,CAAQ,CAAA;EACrB,GAAA,CAAI,KAAA,EAAO,CAAA;EACX,MAAA,IAAU,OAAA,CAAQ,CAAA;EAClB,MAAA;EACA,KAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAGe,qBAAA;EACf,OAAA;EACA,iBAAA;EACA,SAAA;AAAA;AAAA,UAGe,qCAAA,wBAEP,qBAAqB;EAC7B,YAAA,GAAe,aAAA;AAAA;AAAA,iBAGD,cAAA,IAAkB,KAAA,QAAa,OAAA,CAAQ,CAAA,IAAK,cAAA,CAAe,CAAA;AAAA,iBAC3D,cAAA,IACd,KAAA,QAAa,OAAA,CAAQ,CAAA,GACrB,OAAA,EAAS,qBAAA,GACR,cAAA,CAAe,CAAA;AAAA,iBACF,cAAA,mBACd,KAAA,QAAa,OAAA,CAAQ,CAAA,GACrB,OAAA,EAAS,qCAAA,CAAsC,aAAA,IAC9C,cAAA,CAAe,CAAA,EAAG,aAAA;AAAA,UAkLJ,mBAAA,kBAAqC,IAAA,CACpD,cAAA,CAAe,gBAAA,CAAiB,CAAA;EAGhC,GAAA,CAAI,KAAA,EAAO,CAAA;AAAA;AAAA,UAGI,0BAAA,YAAsC,qBAAqB;EAC1E,YAAA,GAAe,CAAA;AAAA;AAAA,iBAGD,mBAAA,IACd,KAAA,QAAa,OAAA,CAAQ,CAAA,KACrB,OAAA,GAAU,0BAAA,CAA2B,CAAA,IACpC,mBAAA,CAAoB,CAAA;AAAA,KAIX,mBAAA,MACV,CAAA,SAAU,mBAAA,YAA+B,CAAA,KAAM,CAAA,SAAU,cAAA,YAA0B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-observable-
|
|
1
|
+
{"version":3,"file":"lazy-observable-Ce5e1Izm.mjs","names":[],"sources":["../src/lazy-observable/lazy-observable.ts"],"sourcesContent":["import {\n _allowStateChanges,\n autorun,\n type IObservableArray,\n type IReactionDisposer,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n} from \"mobx\";\n\nexport interface LazyObservable<T = any, TInitialValue = T | undefined> {\n value: TInitialValue;\n reset(): TInitialValue;\n getOrLoad(): Promise<T>;\n set(value: T): void;\n reload(): Promise<T>;\n status: \"init\" | \"loading\" | \"loaded\" | \"error\";\n error: unknown;\n loading: boolean;\n loaded: boolean;\n}\n\nexport interface LazyObservableOptions {\n shallow?: boolean;\n resetOnUnobserved?: \"never\" | \"always\" | number;\n debugName?: string;\n}\n\nexport interface LazyObservableOptionsWithInitialValue<\n TInitialValue,\n> extends LazyObservableOptions {\n initialValue?: TInitialValue;\n}\n\nexport function lazyObservable<T>(fetch: () => Promise<T>): LazyObservable<T>;\nexport function lazyObservable<T>(\n fetch: () => Promise<T>,\n options: LazyObservableOptions,\n): LazyObservable<T>;\nexport function lazyObservable<T, TInitialValue>(\n fetch: () => Promise<T>,\n options: LazyObservableOptionsWithInitialValue<TInitialValue>,\n): LazyObservable<T, TInitialValue>;\n\nexport function lazyObservable<T>(\n fetch: () => Promise<T>,\n options?: LazyObservableOptionsWithInitialValue<T>,\n): LazyObservable<T> {\n const value = observable.box<T>(options?.initialValue, { deep: !options?.shallow });\n const status = observable.box<LazyObservable<T>[\"status\"]>(\"init\");\n const error = observable.box<unknown>(undefined);\n\n let resetTimer: NodeJS.Timeout;\n let autorunDisposer: IReactionDisposer;\n\n let promise: Promise<T> | undefined;\n let promiseResolve: ((value: T) => void) | undefined;\n let promiseReject: ((value: unknown) => void) | undefined;\n\n const getOrCreatePromise = () => {\n if (promise && status.get() !== \"loaded\" && status.get() !== \"error\") {\n return promise;\n }\n promise = new Promise<T>((resolve, reject) => {\n promiseResolve = resolve;\n promiseReject = reject;\n });\n return promise;\n };\n\n const reset = () => {\n clearTimeout(resetTimer);\n autorunDisposer?.();\n _allowStateChanges(true, () => {\n value.set(options?.initialValue);\n status.set(\"init\");\n error.set(undefined);\n });\n return options?.initialValue;\n };\n\n const load = (): void => {\n if (status.get() === \"loading\") return;\n clearTimeout(resetTimer);\n\n // TODO: this disposer is unreliable while we are using\n // babel to convert all async function to generators\n autorunDisposer?.();\n autorunDisposer = autorun(() => {\n _allowStateChanges(true, () => {\n status.set(\"loading\");\n error.set(undefined);\n });\n\n let fetchPromise: Promise<T>;\n try {\n fetchPromise = fetch();\n } catch (e) {\n _allowStateChanges(true, () => {\n error.set(e);\n status.set(\"error\");\n promiseReject?.(e);\n });\n return;\n }\n\n fetchPromise\n .then((newValue) => {\n _allowStateChanges(true, () => {\n value.set(newValue);\n status.set(\"loaded\");\n promiseResolve?.(newValue);\n });\n })\n .catch((e) => {\n _allowStateChanges(true, () => {\n error.set(e);\n status.set(\"error\");\n promiseReject?.(e);\n });\n });\n });\n };\n\n // TODO: should this resolve any promises?\n const set = (val: T): void => {\n _allowStateChanges(true, () => {\n value.set(val);\n status.set(\"loaded\");\n });\n };\n\n let observedCount = 0;\n\n const onObserved = () => {\n observedCount = Math.min(2, observedCount + 1);\n\n // only consider observed the first time this handler gets called\n if (observedCount !== 1) return;\n\n if (options?.debugName) {\n console.log(`lazyObservable ${options.debugName}`, \"observed\");\n }\n clearTimeout(resetTimer);\n if (status.get() === \"error\" || status.get() === \"init\") {\n load();\n }\n };\n\n const onUnobserved = () => {\n observedCount = Math.max(0, observedCount - 1);\n\n // only consider unobserved when count reaches zero\n if (observedCount) return;\n\n if (options?.debugName) {\n console.log(`lazyObservable ${options.debugName}`, \"unobserved\");\n }\n\n // Errors are never cached regardless of resetOnUnobserved — failure state\n // shouldn't persist across mounts, only successfully loaded values should.\n if (status.get() === \"error\") {\n _allowStateChanges(true, () => {\n status.set(\"init\");\n error.set(undefined);\n });\n return;\n }\n\n if (options?.resetOnUnobserved === \"never\") {\n return;\n } else if (typeof options?.resetOnUnobserved === \"number\") {\n resetTimer = setTimeout(() => {\n reset();\n }, options?.resetOnUnobserved);\n } else {\n reset();\n }\n };\n\n onBecomeObserved(value, onObserved);\n onBecomeObserved(status, onObserved);\n onBecomeObserved(error, onObserved);\n\n onBecomeUnobserved(value, onUnobserved);\n onBecomeUnobserved(status, onUnobserved);\n onBecomeUnobserved(error, onUnobserved);\n\n return {\n get value() {\n return value.get();\n },\n get status() {\n return status.get();\n },\n get error() {\n return error.get();\n },\n get loading() {\n return status.get() === \"loading\";\n },\n get loaded() {\n return status.get() === \"loaded\";\n },\n reload() {\n const promise = getOrCreatePromise();\n load();\n return promise;\n },\n getOrLoad() {\n // TODO: this assertion shouldn't be necessary\n // consider fixing box/value type\n if (this.loaded) return Promise.resolve(value.get() as Promise<T>);\n return this.reload();\n },\n set,\n reset,\n };\n}\n\nexport interface LazyObservableArray<T = any> extends Omit<\n LazyObservable<IObservableArray<T>>,\n \"set\"\n> {\n set(value: T[]): void;\n}\n\nexport interface LazyObservableArrayOptions<T> extends LazyObservableOptions {\n initialValue?: T[];\n}\n\nexport function lazyObservableArray<T>(\n fetch: () => Promise<T[]>,\n options?: LazyObservableArrayOptions<T>,\n): LazyObservableArray<T> {\n return lazyObservable(fetch, { initialValue: [], ...options }) as LazyObservableArray<T>;\n}\n\nexport type InferLazyObservable<O> =\n O extends LazyObservableArray<infer T> ? T[] : O extends LazyObservable<infer T> ? T : never;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as LazyObservableOptions, c as lazyObservableArray, i as LazyObservableArrayOptions, n as LazyObservable, o as LazyObservableOptionsWithInitialValue, r as LazyObservableArray, s as lazyObservable, t as InferLazyObservable } from "./lazy-observable-
|
|
1
|
+
import { a as LazyObservableOptions, c as lazyObservableArray, i as LazyObservableArrayOptions, n as LazyObservable, o as LazyObservableOptionsWithInitialValue, r as LazyObservableArray, s as lazyObservable, t as InferLazyObservable } from "./lazy-observable-Bx58qmfb.mjs";
|
|
2
2
|
import React from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/lazy-observable/components/lazy-observer.d.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-observable.d.mts","names":[],"sources":["../src/lazy-observable/components/lazy-observer.tsx"],"mappings":";;;;KAQK,EAAA,GAAK,cAAA,GAAiB,
|
|
1
|
+
{"version":3,"file":"lazy-observable.d.mts","names":[],"sources":["../src/lazy-observable/components/lazy-observer.tsx"],"mappings":";;;;KAQK,EAAA,GAAK,cAAA,GAAiB,mBAAmB;AAAA,KAEzC,YAAA,WAAuB,EAAA,oBACd,CAAA,GAAI,mBAAA,CAAoB,CAAA,CAAE,CAAA;AAAA,UAGvB,qBAAA;EACf,WAAA,GAAc,KAAA,CAAM,SAAS;AAAA;AAAA,UAGd,sBAAA,WAAiC,EAAA,YAAc,qBAAA;EAC9D,OAAA,MAAa,CAAA;EACb,QAAA,MAAc,KAAA,EAAO,YAAA,CAAa,CAAA,MAAO,KAAA,CAAM,SAAA;AAAA;AAAA,UAGhC,uBAAA,WAAkC,EAAA,UAAY,qBAAA;EAC7D,OAAA,EAAS,CAAA;EACT,QAAA,GAAW,KAAA,EAAO,mBAAA,CAAoB,CAAA,MAAO,KAAA,CAAM,SAAA;AAAA;AAAA,iBAGrC,YAAA,WAAuB,EAAA,IAAM,KAAA,EAAO,sBAAA,CAAuB,CAAA,IAAK,KAAA,CAAM,SAAA;AAAA,iBACtE,YAAA,WAAuB,cAAA,EACrC,KAAA,EAAO,uBAAA,CAAwB,CAAA,IAC9B,KAAA,CAAM,SAAA"}
|
package/dist/lazy-observable.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as lazyObservableArray, t as lazyObservable } from "./lazy-observable-
|
|
1
|
+
import { n as lazyObservableArray, t as lazyObservable } from "./lazy-observable-Ce5e1Izm.mjs";
|
|
2
2
|
import { Observer } from "mobx-react-lite";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-observable.mjs","names":[],"sources":["../src/lazy-observable/components/lazy-observer.tsx"],"sourcesContent":["import type { InferLazyObservable, LazyObservable, LazyObservableArray } from \"../lazy-observable\";\nimport { Observer } from \"mobx-react-lite\";\nimport type React from \"react\";\n\nfunction ThrowError({ error }: { error: unknown }): never {\n throw error;\n}\n\ntype LO = LazyObservable | LazyObservableArray;\n\ntype ObserveTuple<O extends LO[]> = {\n [K in keyof O]: InferLazyObservable<O[K]>;\n};\n\nexport interface LazyObserverBaseProps {\n placeholder?: React.ReactNode;\n}\n\nexport interface LazyObserverTupleProps<O extends LO[]> extends LazyObserverBaseProps {\n observe: [...O];\n children: (...value: ObserveTuple<O>) => React.ReactNode;\n}\n\nexport interface LazyObserverSingleProps<O extends LO> extends LazyObserverBaseProps {\n observe: O;\n children: (value: InferLazyObservable<O>) => React.ReactNode;\n}\n\nexport function LazyObserver<O extends LO[]>(props: LazyObserverTupleProps<O>): React.ReactNode;\nexport function LazyObserver<O extends LazyObservable>(\n props: LazyObserverSingleProps<O>,\n): React.ReactNode;\nexport function LazyObserver(\n props: LazyObserverTupleProps<any> | LazyObserverSingleProps<any>,\n): React.ReactNode {\n const { observe, placeholder, children } = props;\n const observeAsArray = [observe].flat() as LO[];\n return (\n <Observer>\n {() => {\n const failed = observeAsArray.find((o) => o.status === \"error\");\n if (failed) return <ThrowError error={failed.error} />;\n if (!observeAsArray.every((o) => o.loaded)) return placeholder;\n return (children as any)(...observeAsArray.map((o) => o.value));\n }}\n </Observer>\n );\n}\n"],"mappings":";;;;;AAIA,SAAS,WAAW,EAAE,SAAoC;
|
|
1
|
+
{"version":3,"file":"lazy-observable.mjs","names":[],"sources":["../src/lazy-observable/components/lazy-observer.tsx"],"sourcesContent":["import type { InferLazyObservable, LazyObservable, LazyObservableArray } from \"../lazy-observable\";\nimport { Observer } from \"mobx-react-lite\";\nimport type React from \"react\";\n\nfunction ThrowError({ error }: { error: unknown }): never {\n throw error;\n}\n\ntype LO = LazyObservable | LazyObservableArray;\n\ntype ObserveTuple<O extends LO[]> = {\n [K in keyof O]: InferLazyObservable<O[K]>;\n};\n\nexport interface LazyObserverBaseProps {\n placeholder?: React.ReactNode;\n}\n\nexport interface LazyObserverTupleProps<O extends LO[]> extends LazyObserverBaseProps {\n observe: [...O];\n children: (...value: ObserveTuple<O>) => React.ReactNode;\n}\n\nexport interface LazyObserverSingleProps<O extends LO> extends LazyObserverBaseProps {\n observe: O;\n children: (value: InferLazyObservable<O>) => React.ReactNode;\n}\n\nexport function LazyObserver<O extends LO[]>(props: LazyObserverTupleProps<O>): React.ReactNode;\nexport function LazyObserver<O extends LazyObservable>(\n props: LazyObserverSingleProps<O>,\n): React.ReactNode;\nexport function LazyObserver(\n props: LazyObserverTupleProps<any> | LazyObserverSingleProps<any>,\n): React.ReactNode {\n const { observe, placeholder, children } = props;\n const observeAsArray = [observe].flat() as LO[];\n return (\n <Observer>\n {() => {\n const failed = observeAsArray.find((o) => o.status === \"error\");\n if (failed) return <ThrowError error={failed.error} />;\n if (!observeAsArray.every((o) => o.loaded)) return placeholder;\n return (children as any)(...observeAsArray.map((o) => o.value));\n }}\n </Observer>\n );\n}\n"],"mappings":";;;;;AAIA,SAAS,WAAW,EAAE,SAAoC;CACxD,MAAM;AACR;AA0BA,SAAgB,aACd,OACiB;CACjB,MAAM,EAAE,SAAS,aAAa,aAAa;CAC3C,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC,KAAK;CACtC,OACE,oBAAC,UAAD,kBACS;EACL,MAAM,SAAS,eAAe,MAAM,MAAM,EAAE,WAAW,OAAO;EAC9D,IAAI,QAAQ,OAAO,oBAAC,YAAD,EAAY,OAAO,OAAO,MAAQ;EACrD,IAAI,CAAC,eAAe,OAAO,MAAM,EAAE,MAAM,GAAG,OAAO;EACnD,OAAQ,SAAiB,GAAG,eAAe,KAAK,MAAM,EAAE,KAAK,CAAC;CAChE,EACQ;AAEd"}
|
package/dist/model.d.mts
CHANGED
package/dist/model.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.mts","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"mappings":";;;;;;UASiB,UAAA;EACf,MAAA,EAAQ,KAAA,EAAO,
|
|
1
|
+
{"version":3,"file":"model.d.mts","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"mappings":"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"}
|
package/dist/model.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as lazyObservableArray } from "./lazy-observable-
|
|
1
|
+
import { n as lazyObservableArray } from "./lazy-observable-Ce5e1Izm.mjs";
|
|
2
2
|
import { action, makeObservable, observable, runInAction, toJS } from "mobx";
|
|
3
3
|
import * as Value from "typebox/value";
|
|
4
4
|
import * as T from "typebox";
|
package/dist/model.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.mjs","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"sourcesContent":["import { action, makeObservable, observable, runInAction, toJS, type AnnotationsMap } from \"mobx\";\nimport * as T from \"typebox\";\nimport * as Value from \"typebox/value\";\n\n// -----------------------------------------------------------------------------\n// Public structural contract\n// -----------------------------------------------------------------------------\n\n/** Minimal interface a model's host store must satisfy. */\nexport interface ModelStore<M> {\n remove?(model: M): void;\n}\n\n/**\n * Root schema for a model: a single object (`makeModel`) or a discriminated\n * union of objects (`makeUnionModel`). Shared by both factories and by\n * `makeStore`, which accepts either.\n */\nexport type ModelSchema = T.TObject | T.TUnion<T.TObject[]>;\n\n/**\n * Every property name across the schema. For a union this is the merged set of\n * all variants' keys, so all of them are made observable up front — that keeps\n * `setData` reactive even when it switches the active variant. `toJSON` runs\n * `Value.Clean` to emit only the keys of the variant the data currently matches.\n */\nfunction getPropertyNames(schema: ModelSchema): string[] {\n if (!T.IsUnion(schema)) return Object.keys(schema.properties);\n const names = new Set<string>();\n for (const variant of schema.anyOf) {\n for (const key of Object.keys((variant as T.TObject).properties)) names.add(key);\n }\n return [...names];\n}\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\ntype Resource<S extends ModelSchema> = T.Static<S>;\n\ntype KeyShape<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n> = K extends readonly [] ? undefined : Pick<Resource<S>, K[number]>;\n\ntype KeyedFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (...args: any[]) => Promise<R>\n : (params: KeyShape<S, K>, ...rest: any[]) => Promise<R>;\n\ntype KeyedBodyFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (body: any, ...rest: any[]) => Promise<R>\n : (params: KeyShape<S, K>, body: any, ...rest: any[]) => Promise<R>;\n\n// Strip the first arg when keys is non-empty — model methods don't take the params.\ntype StripParams<K extends readonly any[], F> = K extends readonly []\n ? F\n : F extends (params: any, ...rest: infer R) => infer Ret\n ? (...args: R) => Ret\n : never;\n\n// Replace a function's Promise return with Promise<R>.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\ntype ReservedActionKey = \"reload\" | \"update\" | \"delete\" | \"setData\" | \"toJSON\" | \"store\";\n\ntype ActionsConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> = {\n [name: string]: KeyedFn<S, K, Resource<S>>;\n} & { [Key in ReservedActionKey]?: never };\n\nexport interface ModelConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> {\n keys: K;\n reload?: KeyedFn<S, K, Resource<S>>;\n update?: KeyedBodyFn<S, K, Resource<S>>;\n delete?: KeyedFn<S, K, any>;\n actions?: ActionsConfig<S, K>;\n}\n\n// Instance method shape from config. Self-mutating methods return Promise<any>;\n// the instance is mutated in place via setData, so callers typically read fields\n// off the same reference instead of chaining the return.\ntype ModelMethods<K extends readonly any[], Cfg> = (Cfg extends { reload: infer F }\n ? { reload: ReplaceReturn<StripParams<K, F>, any> }\n : {}) &\n (Cfg extends { update: infer F } ? { update: ReplaceReturn<StripParams<K, F>, any> } : {}) &\n (Cfg extends { delete: infer F } ? { delete: StripParams<K, F> } : {}) &\n (Cfg extends { actions: infer A }\n ? {\n [N in keyof A]: A[N] extends (...args: any[]) => any\n ? ReplaceReturn<StripParams<K, A[N]>, any>\n : never;\n }\n : {});\n\n// -----------------------------------------------------------------------------\n// Constructor type\n// -----------------------------------------------------------------------------\n\ntype ModelInstance<S extends ModelSchema, K extends readonly any[], Cfg> = Resource<S> & {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n} & ModelMethods<K, Cfg>;\n\nexport type ModelConstructor<S extends ModelSchema, K extends readonly any[], Cfg> = {\n new (data: Resource<S>, store?: ModelStore<any>): ModelInstance<S, K, Cfg>;\n readonly schema: S;\n};\n\n// -----------------------------------------------------------------------------\n// Shared class builder\n// -----------------------------------------------------------------------------\n\n// Builds the observable model class used by both makeModel and makeUnionModel.\n// Handles object and union schemas at runtime; the public factories layer the\n// appropriate types (and, for unions, the `is`/`as` guards) on top.\nfunction createModelClass(schema: ModelSchema, config?: ModelConfig<any, any>): any {\n const keys = (config?.keys ?? []) as readonly PropertyKey[];\n const isUnion = T.IsUnion(schema);\n const propertyNames = getPropertyNames(schema);\n\n abstract class BaseModel {\n static readonly schema = schema;\n\n readonly store?: ModelStore<any>;\n\n constructor(data: any, store?: ModelStore<any>) {\n this.store = store;\n\n // Make every property of every variant observable up front, so `setData`\n // stays reactive even when it switches the active variant. Foreign-variant\n // fields sit as `undefined`; TypeScript hides them, and `toJSON` cleans them out.\n const annotations: Record<string, any> = {};\n for (const key of propertyNames) {\n Object.defineProperty(this, key, {\n value: (data as any)[key],\n enumerable: true,\n configurable: true,\n writable: true,\n });\n annotations[key] = observable.ref;\n }\n\n makeObservable(this, {\n ...annotations,\n setData: action,\n ...(this as any).getMobxAnnotations?.(),\n });\n }\n\n /**\n * Replace the model's data with a complete resource. Every property is\n * reassigned (fields absent from `data` — e.g. another variant's — become\n * `undefined`), so the model always holds a coherent, whole variant rather\n * than a partial merge that could mix fields across the union.\n */\n setData(data: any): void {\n for (const key of propertyNames) {\n (this as any)[key] = (data as any)[key];\n }\n }\n\n /**\n * Build the params object passed as the first arg to keyed API methods.\n * Default extracts each property in `keys` from the model. Override on a\n * subclass when the model field name differs from the API param name, or\n * to construct composite params from derived values.\n */\n buildParams(): any {\n if (keys.length === 0) return undefined;\n const data = this as any;\n return Object.fromEntries(keys.map((k) => [k, data[k]]));\n }\n\n toJSON(): any {\n const data = this as any;\n const snapshot = propertyNames.reduce(\n (obj, key) => {\n if (data[key] !== undefined) obj[key] = toJS(data[key]);\n return obj;\n },\n {} as Record<string, any>,\n );\n // For a union, strip any fields not belonging to the variant the current\n // data matches (e.g. a stale field left over from a previous variant).\n return isUnion ? Value.Clean(schema, snapshot) : snapshot;\n }\n }\n\n const proto = BaseModel.prototype as any;\n\n if (config?.reload) {\n const reload = config.reload as (...args: any[]) => Promise<any>;\n proto.reload = async function (...rest: any[]) {\n const params = this.buildParams();\n const data = params === undefined ? await reload(...rest) : await reload(params, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.update) {\n const update = config.update as (...args: any[]) => Promise<any>;\n proto.update = async function (body: any, ...rest: any[]) {\n const params = this.buildParams();\n const data =\n params === undefined ? await update(body, ...rest) : await update(params, body, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.delete) {\n const del = config.delete as (...args: any[]) => Promise<any>;\n proto.delete = async function (...rest: any[]) {\n const params = this.buildParams();\n const result = params === undefined ? await del(...rest) : await del(params, ...rest);\n this.store?.remove?.(this);\n return result;\n };\n }\n\n if (config?.actions) {\n for (const [name, fn] of Object.entries(config.actions)) {\n const call = fn as (...args: any[]) => Promise<any>;\n proto[name] = async function (body?: any, ...rest: any[]) {\n const params = this.buildParams();\n let data: any;\n if (params === undefined) {\n data = body === undefined ? await call() : await call(body, ...rest);\n } else {\n data = body === undefined ? await call(params) : await call(params, body, ...rest);\n }\n runInAction(() => this.setData(data));\n return this;\n };\n }\n }\n\n return BaseModel;\n}\n\n// -----------------------------------------------------------------------------\n// makeModel (single object schemas)\n// -----------------------------------------------------------------------------\n\nexport function makeModel<S extends T.TObject>(schema: S): ModelConstructor<S, readonly [], {}>;\nexport function makeModel<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, config: Cfg): ModelConstructor<S, K, Cfg>;\nexport function makeModel<S extends T.TObject>(\n schema: S,\n config?: ModelConfig<S, readonly (keyof Resource<S>)[]>,\n): any {\n return createModelClass(schema, config);\n}\n\n// -----------------------------------------------------------------------------\n// makeUnionModel (discriminated union schemas)\n// -----------------------------------------------------------------------------\n\ntype UnionSchema = T.TUnion<T.TObject[]>;\n\n// Properties common to every variant (`keyof` a union resolves to shared keys).\ntype SharedFields<S extends UnionSchema> = { [K in keyof Resource<S>]: Resource<S>[K] };\n\n// The full static shape of the variant whose discriminator `D` equals `V`.\ntype VariantFields<S extends UnionSchema, D extends keyof Resource<S>, V> = Extract<\n Resource<S>,\n Record<D, V>\n>;\n\n// The members makeUnionModel adds. An interface (not a type-alias literal) so the\n// polymorphic `this` in `is`/`as` is allowed; at a call site `this` resolves to\n// the full instance, so the guard reveals the variant's fields on it.\ninterface UnionModelMembers<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n> {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n /** Type guard: true when the discriminator equals `value`, revealing that variant's fields on this same instance. */\n is<V extends Resource<S>[D]>(value: V): this is this & VariantFields<S, D, V>;\n /** This instance narrowed to the `value` variant (fields exposed directly), or `undefined` if it doesn't match. */\n as<V extends Resource<S>[D]>(value: V): (this & VariantFields<S, D, V>) | undefined;\n}\n\n// Base instance exposes only the shared fields (a single object type, so it can\n// be subclassed). Variant-specific fields exist at runtime but are revealed on\n// the type only through `is`/`as`.\ntype UnionModelInstance<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = SharedFields<S> & UnionModelMembers<S, D, K> & ModelMethods<K, Cfg>;\n\nexport type UnionModelConstructor<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = {\n new (data: Resource<S>, store?: ModelStore<any>): UnionModelInstance<S, D, K, Cfg>;\n readonly schema: S;\n readonly discriminator: D;\n};\n\nexport function makeUnionModel<S extends UnionSchema, D extends keyof Resource<S> & string>(\n schema: S,\n discriminator: D,\n): UnionModelConstructor<S, D, readonly [], {}>;\nexport function makeUnionModel<\n S extends UnionSchema,\n D extends keyof Resource<S> & string,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, discriminator: D, config: Cfg): UnionModelConstructor<S, D, K, Cfg>;\nexport function makeUnionModel(\n schema: UnionSchema,\n discriminator: string,\n config?: ModelConfig<any, any>,\n): any {\n const ModelClass = createModelClass(schema, config);\n ModelClass.discriminator = discriminator;\n\n const proto = ModelClass.prototype as any;\n proto.is = function (value: unknown): boolean {\n return (this as any)[discriminator] === value;\n };\n proto.as = function (value: unknown): unknown {\n return (this as any)[discriminator] === value ? this : undefined;\n };\n\n return ModelClass;\n}\n\nexport type { AnnotationsMap };\n","import * as T from \"typebox\";\nimport { lazyObservableArray, type LazyObservableArray } from \"../lazy-observable/lazy-observable\";\nimport { action, makeObservable, runInAction } from \"mobx\";\nimport type { ModelSchema } from \"./make-model\";\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\n// Replace a function's Promise return with Promise<R> — used so get/create\n// return models rather than the raw API response.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\n// The structural shape passed as `this` inside `transform` — the only member\n// transform ever needs is `remove`, since it's about to construct a model and\n// hand the store reference along to its constructor.\ntype StoreThis<M> = {\n remove(model: M): void;\n};\n\n// Infer M from config: when transform is present use its return type, else use R.\ntype InferModel<R, Cfg> = Cfg extends { transform: (...args: any[]) => infer M } ? M : R;\n\nexport interface StoreConfig<R> {\n transform?: (data: R, store: { remove(model: any): void }) => any;\n get?: (...args: any[]) => Promise<R>;\n getAll?: (...args: any[]) => Promise<R[]>;\n create?: (...args: any[]) => Promise<R>;\n}\n\n// Final store-instance shape, derived from config slots.\ntype StoreInstance<M, Cfg> = {\n remove(model: M): void;\n} & (Cfg extends { get: infer G } ? { get: ReplaceReturn<G, M> } : {}) &\n (Cfg extends { getAll: any } ? { getAll: () => Promise<M[]>; all: LazyObservableArray<M> } : {}) &\n (Cfg extends { create: infer C } ? { create: ReplaceReturn<C, M> } : {});\n\nexport type StoreConstructor<M, Cfg> = {\n new (): StoreInstance<M, Cfg>;\n};\n\n// -----------------------------------------------------------------------------\n// makeStore\n// -----------------------------------------------------------------------------\n\nexport function makeStore<S extends ModelSchema>(schema: S): StoreConstructor<T.Static<S>, {}>;\nexport function makeStore<S extends ModelSchema, Cfg extends StoreConfig<T.Static<S>>>(\n schema: S,\n config: Cfg & ThisType<StoreThis<InferModel<T.Static<S>, Cfg>>>,\n): StoreConstructor<InferModel<T.Static<S>, Cfg>, Cfg>;\nexport function makeStore<S extends ModelSchema>(\n schema: S,\n config?: StoreConfig<T.Static<S>>,\n): StoreConstructor<any, any> {\n type R = T.Static<S>;\n\n class Store {\n private readonly _transform: (data: R) => any;\n\n all?: LazyObservableArray<any>;\n\n constructor() {\n const rawTransform = config?.transform;\n this._transform = rawTransform\n ? (data: R) => rawTransform.call(this, data, this)\n : (data: R) => data as any;\n\n makeObservable<this, \"_transform\">(this, {\n _transform: false,\n remove: action,\n });\n\n if (config?.getAll) {\n this.all = lazyObservableArray(async () => {\n const items = await (config.getAll as (...args: any[]) => Promise<R[]>)();\n return items.map((item) => this._transform(item));\n });\n }\n }\n\n remove(model: any): void {\n this.all?.value?.remove?.(model);\n }\n }\n\n const proto = Store.prototype as any;\n\n if (config?.get) {\n const get = config.get;\n proto.get = function (...args: any[]) {\n return get(...args).then((data: R) => this._transform(data));\n };\n }\n\n if (config?.getAll) {\n proto.getAll = function () {\n return this.all.getOrLoad();\n };\n }\n\n if (config?.create) {\n const create = config.create;\n proto.create = async function (...args: any[]) {\n const data = await create(...args);\n return runInAction(() => {\n const model = this._transform(data);\n if (this.all) this.all.value.unshift(model);\n return model;\n });\n };\n }\n\n return Store as unknown as StoreConstructor<any, any>;\n}\n\nexport type { LazyObservableArray };\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAS,iBAAiB,QAA+B;AACvD,KAAI,CAAC,EAAE,QAAQ,OAAO,CAAE,QAAO,OAAO,KAAK,OAAO,WAAW;CAC7D,MAAM,wBAAQ,IAAI,KAAa;AAC/B,MAAK,MAAM,WAAW,OAAO,MAC3B,MAAK,MAAM,OAAO,OAAO,KAAM,QAAsB,WAAW,CAAE,OAAM,IAAI,IAAI;AAElF,QAAO,CAAC,GAAG,MAAM;;AAgGnB,SAAS,iBAAiB,QAAqB,QAAqC;CAClF,MAAM,OAAQ,QAAQ,QAAQ,EAAE;CAChC,MAAM,UAAU,EAAE,QAAQ,OAAO;CACjC,MAAM,gBAAgB,iBAAiB,OAAO;CAE9C,MAAe,UAAU;EACvB,OAAgB,SAAS;EAEzB,AAAS;EAET,YAAY,MAAW,OAAyB;AAC9C,QAAK,QAAQ;GAKb,MAAM,cAAmC,EAAE;AAC3C,QAAK,MAAM,OAAO,eAAe;AAC/B,WAAO,eAAe,MAAM,KAAK;KAC/B,OAAQ,KAAa;KACrB,YAAY;KACZ,cAAc;KACd,UAAU;KACX,CAAC;AACF,gBAAY,OAAO,WAAW;;AAGhC,kBAAe,MAAM;IACnB,GAAG;IACH,SAAS;IACT,GAAI,KAAa,sBAAsB;IACxC,CAAC;;;;;;;;EASJ,QAAQ,MAAiB;AACvB,QAAK,MAAM,OAAO,cAChB,CAAC,KAAa,OAAQ,KAAa;;;;;;;;EAUvC,cAAmB;AACjB,OAAI,KAAK,WAAW,EAAG,QAAO;GAC9B,MAAM,OAAO;AACb,UAAO,OAAO,YAAY,KAAK,KAAK,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;;EAG1D,SAAc;GACZ,MAAM,OAAO;GACb,MAAM,WAAW,cAAc,QAC5B,KAAK,QAAQ;AACZ,QAAI,KAAK,SAAS,OAAW,KAAI,OAAO,KAAK,KAAK,KAAK;AACvD,WAAO;MAET,EAAE,CACH;AAGD,UAAO,UAAU,MAAM,MAAM,QAAQ,SAAS,GAAG;;;CAIrD,MAAM,QAAQ,UAAU;AAExB,KAAI,QAAQ,QAAQ;EAClB,MAAM,SAAS,OAAO;AACtB,QAAM,SAAS,eAAgB,GAAG,MAAa;GAC7C,MAAM,SAAS,KAAK,aAAa;GACjC,MAAM,OAAO,WAAW,SAAY,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,OAAO,QAAQ,GAAG,KAAK;AACzF,qBAAkB,KAAK,QAAQ,KAAK,CAAC;AACrC,UAAO;;;AAIX,KAAI,QAAQ,QAAQ;EAClB,MAAM,SAAS,OAAO;AACtB,QAAM,SAAS,eAAgB,MAAW,GAAG,MAAa;GACxD,MAAM,SAAS,KAAK,aAAa;GACjC,MAAM,OACJ,WAAW,SAAY,MAAM,OAAO,MAAM,GAAG,KAAK,GAAG,MAAM,OAAO,QAAQ,MAAM,GAAG,KAAK;AAC1F,qBAAkB,KAAK,QAAQ,KAAK,CAAC;AACrC,UAAO;;;AAIX,KAAI,QAAQ,QAAQ;EAClB,MAAM,MAAM,OAAO;AACnB,QAAM,SAAS,eAAgB,GAAG,MAAa;GAC7C,MAAM,SAAS,KAAK,aAAa;GACjC,MAAM,SAAS,WAAW,SAAY,MAAM,IAAI,GAAG,KAAK,GAAG,MAAM,IAAI,QAAQ,GAAG,KAAK;AACrF,QAAK,OAAO,SAAS,KAAK;AAC1B,UAAO;;;AAIX,KAAI,QAAQ,QACV,MAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,OAAO,QAAQ,EAAE;EACvD,MAAM,OAAO;AACb,QAAM,QAAQ,eAAgB,MAAY,GAAG,MAAa;GACxD,MAAM,SAAS,KAAK,aAAa;GACjC,IAAI;AACJ,OAAI,WAAW,OACb,QAAO,SAAS,SAAY,MAAM,MAAM,GAAG,MAAM,KAAK,MAAM,GAAG,KAAK;OAEpE,QAAO,SAAS,SAAY,MAAM,KAAK,OAAO,GAAG,MAAM,KAAK,QAAQ,MAAM,GAAG,KAAK;AAEpF,qBAAkB,KAAK,QAAQ,KAAK,CAAC;AACrC,UAAO;;;AAKb,QAAO;;AAaT,SAAgB,UACd,QACA,QACK;AACL,QAAO,iBAAiB,QAAQ,OAAO;;AAoEzC,SAAgB,eACd,QACA,eACA,QACK;CACL,MAAM,aAAa,iBAAiB,QAAQ,OAAO;AACnD,YAAW,gBAAgB;CAE3B,MAAM,QAAQ,WAAW;AACzB,OAAM,KAAK,SAAU,OAAyB;AAC5C,SAAQ,KAAa,mBAAmB;;AAE1C,OAAM,KAAK,SAAU,OAAyB;AAC5C,SAAQ,KAAa,mBAAmB,QAAQ,OAAO;;AAGzD,QAAO;;;;;AC5ST,SAAgB,UACd,QACA,QAC4B;CAG5B,MAAM,MAAM;EACV,AAAiB;EAEjB;EAEA,cAAc;GACZ,MAAM,eAAe,QAAQ;AAC7B,QAAK,aAAa,gBACb,SAAY,aAAa,KAAK,MAAM,MAAM,KAAK,IAC/C,SAAY;AAEjB,kBAAmC,MAAM;IACvC,YAAY;IACZ,QAAQ;IACT,CAAC;AAEF,OAAI,QAAQ,OACV,MAAK,MAAM,oBAAoB,YAAY;AAEzC,YADc,MAAO,OAAO,QAA6C,EAC5D,KAAK,SAAS,KAAK,WAAW,KAAK,CAAC;KACjD;;EAIN,OAAO,OAAkB;AACvB,QAAK,KAAK,OAAO,SAAS,MAAM;;;CAIpC,MAAM,QAAQ,MAAM;AAEpB,KAAI,QAAQ,KAAK;EACf,MAAM,MAAM,OAAO;AACnB,QAAM,MAAM,SAAU,GAAG,MAAa;AACpC,UAAO,IAAI,GAAG,KAAK,CAAC,MAAM,SAAY,KAAK,WAAW,KAAK,CAAC;;;AAIhE,KAAI,QAAQ,OACV,OAAM,SAAS,WAAY;AACzB,SAAO,KAAK,IAAI,WAAW;;AAI/B,KAAI,QAAQ,QAAQ;EAClB,MAAM,SAAS,OAAO;AACtB,QAAM,SAAS,eAAgB,GAAG,MAAa;GAC7C,MAAM,OAAO,MAAM,OAAO,GAAG,KAAK;AAClC,UAAO,kBAAkB;IACvB,MAAM,QAAQ,KAAK,WAAW,KAAK;AACnC,QAAI,KAAK,IAAK,MAAK,IAAI,MAAM,QAAQ,MAAM;AAC3C,WAAO;KACP;;;AAIN,QAAO"}
|
|
1
|
+
{"version":3,"file":"model.mjs","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"sourcesContent":["import { action, makeObservable, observable, runInAction, toJS, type AnnotationsMap } from \"mobx\";\nimport * as T from \"typebox\";\nimport * as Value from \"typebox/value\";\n\n// -----------------------------------------------------------------------------\n// Public structural contract\n// -----------------------------------------------------------------------------\n\n/** Minimal interface a model's host store must satisfy. */\nexport interface ModelStore<M> {\n remove?(model: M): void;\n}\n\n/**\n * Root schema for a model: a single object (`makeModel`) or a discriminated\n * union of objects (`makeUnionModel`). Shared by both factories and by\n * `makeStore`, which accepts either.\n */\nexport type ModelSchema = T.TObject | T.TUnion<T.TObject[]>;\n\n/**\n * Every property name across the schema. For a union this is the merged set of\n * all variants' keys, so all of them are made observable up front — that keeps\n * `setData` reactive even when it switches the active variant. `toJSON` runs\n * `Value.Clean` to emit only the keys of the variant the data currently matches.\n */\nfunction getPropertyNames(schema: ModelSchema): string[] {\n if (!T.IsUnion(schema)) return Object.keys(schema.properties);\n const names = new Set<string>();\n for (const variant of schema.anyOf) {\n for (const key of Object.keys((variant as T.TObject).properties)) names.add(key);\n }\n return [...names];\n}\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\ntype Resource<S extends ModelSchema> = T.Static<S>;\n\ntype KeyShape<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n> = K extends readonly [] ? undefined : Pick<Resource<S>, K[number]>;\n\ntype KeyedFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (...args: any[]) => Promise<R>\n : (params: KeyShape<S, K>, ...rest: any[]) => Promise<R>;\n\ntype KeyedBodyFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (body: any, ...rest: any[]) => Promise<R>\n : (params: KeyShape<S, K>, body: any, ...rest: any[]) => Promise<R>;\n\n// Strip the first arg when keys is non-empty — model methods don't take the params.\ntype StripParams<K extends readonly any[], F> = K extends readonly []\n ? F\n : F extends (params: any, ...rest: infer R) => infer Ret\n ? (...args: R) => Ret\n : never;\n\n// Replace a function's Promise return with Promise<R>.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\ntype ReservedActionKey = \"reload\" | \"update\" | \"delete\" | \"setData\" | \"toJSON\" | \"store\";\n\ntype ActionsConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> = {\n [name: string]: KeyedFn<S, K, Resource<S>>;\n} & { [Key in ReservedActionKey]?: never };\n\nexport interface ModelConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> {\n keys: K;\n reload?: KeyedFn<S, K, Resource<S>>;\n update?: KeyedBodyFn<S, K, Resource<S>>;\n delete?: KeyedFn<S, K, any>;\n actions?: ActionsConfig<S, K>;\n}\n\n// Instance method shape from config. Self-mutating methods return Promise<any>;\n// the instance is mutated in place via setData, so callers typically read fields\n// off the same reference instead of chaining the return.\ntype ModelMethods<K extends readonly any[], Cfg> = (Cfg extends { reload: infer F }\n ? { reload: ReplaceReturn<StripParams<K, F>, any> }\n : {}) &\n (Cfg extends { update: infer F } ? { update: ReplaceReturn<StripParams<K, F>, any> } : {}) &\n (Cfg extends { delete: infer F } ? { delete: StripParams<K, F> } : {}) &\n (Cfg extends { actions: infer A }\n ? {\n [N in keyof A]: A[N] extends (...args: any[]) => any\n ? ReplaceReturn<StripParams<K, A[N]>, any>\n : never;\n }\n : {});\n\n// -----------------------------------------------------------------------------\n// Constructor type\n// -----------------------------------------------------------------------------\n\ntype ModelInstance<S extends ModelSchema, K extends readonly any[], Cfg> = Resource<S> & {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n} & ModelMethods<K, Cfg>;\n\nexport type ModelConstructor<S extends ModelSchema, K extends readonly any[], Cfg> = {\n new (data: Resource<S>, store?: ModelStore<any>): ModelInstance<S, K, Cfg>;\n readonly schema: S;\n};\n\n// -----------------------------------------------------------------------------\n// Shared class builder\n// -----------------------------------------------------------------------------\n\n// Builds the observable model class used by both makeModel and makeUnionModel.\n// Handles object and union schemas at runtime; the public factories layer the\n// appropriate types (and, for unions, the `is`/`as` guards) on top.\nfunction createModelClass(schema: ModelSchema, config?: ModelConfig<any, any>): any {\n const keys = (config?.keys ?? []) as readonly PropertyKey[];\n const isUnion = T.IsUnion(schema);\n const propertyNames = getPropertyNames(schema);\n\n abstract class BaseModel {\n static readonly schema = schema;\n\n readonly store?: ModelStore<any>;\n\n constructor(data: any, store?: ModelStore<any>) {\n this.store = store;\n\n // Make every property of every variant observable up front, so `setData`\n // stays reactive even when it switches the active variant. Foreign-variant\n // fields sit as `undefined`; TypeScript hides them, and `toJSON` cleans them out.\n const annotations: Record<string, any> = {};\n for (const key of propertyNames) {\n Object.defineProperty(this, key, {\n value: (data as any)[key],\n enumerable: true,\n configurable: true,\n writable: true,\n });\n annotations[key] = observable.ref;\n }\n\n makeObservable(this, {\n ...annotations,\n setData: action,\n ...(this as any).getMobxAnnotations?.(),\n });\n }\n\n /**\n * Replace the model's data with a complete resource. Every property is\n * reassigned (fields absent from `data` — e.g. another variant's — become\n * `undefined`), so the model always holds a coherent, whole variant rather\n * than a partial merge that could mix fields across the union.\n */\n setData(data: any): void {\n for (const key of propertyNames) {\n (this as any)[key] = (data as any)[key];\n }\n }\n\n /**\n * Build the params object passed as the first arg to keyed API methods.\n * Default extracts each property in `keys` from the model. Override on a\n * subclass when the model field name differs from the API param name, or\n * to construct composite params from derived values.\n */\n buildParams(): any {\n if (keys.length === 0) return undefined;\n const data = this as any;\n return Object.fromEntries(keys.map((k) => [k, data[k]]));\n }\n\n toJSON(): any {\n const data = this as any;\n const snapshot = propertyNames.reduce(\n (obj, key) => {\n if (data[key] !== undefined) obj[key] = toJS(data[key]);\n return obj;\n },\n {} as Record<string, any>,\n );\n // For a union, strip any fields not belonging to the variant the current\n // data matches (e.g. a stale field left over from a previous variant).\n return isUnion ? Value.Clean(schema, snapshot) : snapshot;\n }\n }\n\n const proto = BaseModel.prototype as any;\n\n if (config?.reload) {\n const reload = config.reload as (...args: any[]) => Promise<any>;\n proto.reload = async function (...rest: any[]) {\n const params = this.buildParams();\n const data = params === undefined ? await reload(...rest) : await reload(params, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.update) {\n const update = config.update as (...args: any[]) => Promise<any>;\n proto.update = async function (body: any, ...rest: any[]) {\n const params = this.buildParams();\n const data =\n params === undefined ? await update(body, ...rest) : await update(params, body, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.delete) {\n const del = config.delete as (...args: any[]) => Promise<any>;\n proto.delete = async function (...rest: any[]) {\n const params = this.buildParams();\n const result = params === undefined ? await del(...rest) : await del(params, ...rest);\n this.store?.remove?.(this);\n return result;\n };\n }\n\n if (config?.actions) {\n for (const [name, fn] of Object.entries(config.actions)) {\n const call = fn as (...args: any[]) => Promise<any>;\n proto[name] = async function (body?: any, ...rest: any[]) {\n const params = this.buildParams();\n let data: any;\n if (params === undefined) {\n data = body === undefined ? await call() : await call(body, ...rest);\n } else {\n data = body === undefined ? await call(params) : await call(params, body, ...rest);\n }\n runInAction(() => this.setData(data));\n return this;\n };\n }\n }\n\n return BaseModel;\n}\n\n// -----------------------------------------------------------------------------\n// makeModel (single object schemas)\n// -----------------------------------------------------------------------------\n\nexport function makeModel<S extends T.TObject>(schema: S): ModelConstructor<S, readonly [], {}>;\nexport function makeModel<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, config: Cfg): ModelConstructor<S, K, Cfg>;\nexport function makeModel<S extends T.TObject>(\n schema: S,\n config?: ModelConfig<S, readonly (keyof Resource<S>)[]>,\n): any {\n return createModelClass(schema, config);\n}\n\n// -----------------------------------------------------------------------------\n// makeUnionModel (discriminated union schemas)\n// -----------------------------------------------------------------------------\n\ntype UnionSchema = T.TUnion<T.TObject[]>;\n\n// Properties common to every variant (`keyof` a union resolves to shared keys).\ntype SharedFields<S extends UnionSchema> = { [K in keyof Resource<S>]: Resource<S>[K] };\n\n// The full static shape of the variant whose discriminator `D` equals `V`.\ntype VariantFields<S extends UnionSchema, D extends keyof Resource<S>, V> = Extract<\n Resource<S>,\n Record<D, V>\n>;\n\n// The members makeUnionModel adds. An interface (not a type-alias literal) so the\n// polymorphic `this` in `is`/`as` is allowed; at a call site `this` resolves to\n// the full instance, so the guard reveals the variant's fields on it.\ninterface UnionModelMembers<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n> {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n /** Type guard: true when the discriminator equals `value`, revealing that variant's fields on this same instance. */\n is<V extends Resource<S>[D]>(value: V): this is this & VariantFields<S, D, V>;\n /** This instance narrowed to the `value` variant (fields exposed directly), or `undefined` if it doesn't match. */\n as<V extends Resource<S>[D]>(value: V): (this & VariantFields<S, D, V>) | undefined;\n}\n\n// Base instance exposes only the shared fields (a single object type, so it can\n// be subclassed). Variant-specific fields exist at runtime but are revealed on\n// the type only through `is`/`as`.\ntype UnionModelInstance<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = SharedFields<S> & UnionModelMembers<S, D, K> & ModelMethods<K, Cfg>;\n\nexport type UnionModelConstructor<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = {\n new (data: Resource<S>, store?: ModelStore<any>): UnionModelInstance<S, D, K, Cfg>;\n readonly schema: S;\n readonly discriminator: D;\n};\n\nexport function makeUnionModel<S extends UnionSchema, D extends keyof Resource<S> & string>(\n schema: S,\n discriminator: D,\n): UnionModelConstructor<S, D, readonly [], {}>;\nexport function makeUnionModel<\n S extends UnionSchema,\n D extends keyof Resource<S> & string,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, discriminator: D, config: Cfg): UnionModelConstructor<S, D, K, Cfg>;\nexport function makeUnionModel(\n schema: UnionSchema,\n discriminator: string,\n config?: ModelConfig<any, any>,\n): any {\n const ModelClass = createModelClass(schema, config);\n ModelClass.discriminator = discriminator;\n\n const proto = ModelClass.prototype as any;\n proto.is = function (value: unknown): boolean {\n return (this as any)[discriminator] === value;\n };\n proto.as = function (value: unknown): unknown {\n return (this as any)[discriminator] === value ? this : undefined;\n };\n\n return ModelClass;\n}\n\nexport type { AnnotationsMap };\n","import * as T from \"typebox\";\nimport { lazyObservableArray, type LazyObservableArray } from \"../lazy-observable/lazy-observable\";\nimport { action, makeObservable, runInAction } from \"mobx\";\nimport type { ModelSchema } from \"./make-model\";\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\n// Replace a function's Promise return with Promise<R> — used so get/create\n// return models rather than the raw API response.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\n// The structural shape passed as `this` inside `transform` — the only member\n// transform ever needs is `remove`, since it's about to construct a model and\n// hand the store reference along to its constructor.\ntype StoreThis<M> = {\n remove(model: M): void;\n};\n\n// Infer M from config: when transform is present use its return type, else use R.\ntype InferModel<R, Cfg> = Cfg extends { transform: (...args: any[]) => infer M } ? M : R;\n\nexport interface StoreConfig<R> {\n transform?: (data: R, store: { remove(model: any): void }) => any;\n get?: (...args: any[]) => Promise<R>;\n getAll?: (...args: any[]) => Promise<R[]>;\n create?: (...args: any[]) => Promise<R>;\n}\n\n// Final store-instance shape, derived from config slots.\ntype StoreInstance<M, Cfg> = {\n remove(model: M): void;\n} & (Cfg extends { get: infer G } ? { get: ReplaceReturn<G, M> } : {}) &\n (Cfg extends { getAll: any } ? { getAll: () => Promise<M[]>; all: LazyObservableArray<M> } : {}) &\n (Cfg extends { create: infer C } ? { create: ReplaceReturn<C, M> } : {});\n\nexport type StoreConstructor<M, Cfg> = {\n new (): StoreInstance<M, Cfg>;\n};\n\n// -----------------------------------------------------------------------------\n// makeStore\n// -----------------------------------------------------------------------------\n\nexport function makeStore<S extends ModelSchema>(schema: S): StoreConstructor<T.Static<S>, {}>;\nexport function makeStore<S extends ModelSchema, Cfg extends StoreConfig<T.Static<S>>>(\n schema: S,\n config: Cfg & ThisType<StoreThis<InferModel<T.Static<S>, Cfg>>>,\n): StoreConstructor<InferModel<T.Static<S>, Cfg>, Cfg>;\nexport function makeStore<S extends ModelSchema>(\n schema: S,\n config?: StoreConfig<T.Static<S>>,\n): StoreConstructor<any, any> {\n type R = T.Static<S>;\n\n class Store {\n private readonly _transform: (data: R) => any;\n\n all?: LazyObservableArray<any>;\n\n constructor() {\n const rawTransform = config?.transform;\n this._transform = rawTransform\n ? (data: R) => rawTransform.call(this, data, this)\n : (data: R) => data as any;\n\n makeObservable<this, \"_transform\">(this, {\n _transform: false,\n remove: action,\n });\n\n if (config?.getAll) {\n this.all = lazyObservableArray(async () => {\n const items = await (config.getAll as (...args: any[]) => Promise<R[]>)();\n return items.map((item) => this._transform(item));\n });\n }\n }\n\n remove(model: any): void {\n this.all?.value?.remove?.(model);\n }\n }\n\n const proto = Store.prototype as any;\n\n if (config?.get) {\n const get = config.get;\n proto.get = function (...args: any[]) {\n return get(...args).then((data: R) => this._transform(data));\n };\n }\n\n if (config?.getAll) {\n proto.getAll = function () {\n return this.all.getOrLoad();\n };\n }\n\n if (config?.create) {\n const create = config.create;\n proto.create = async function (...args: any[]) {\n const data = await create(...args);\n return runInAction(() => {\n const model = this._transform(data);\n if (this.all) this.all.value.unshift(model);\n return model;\n });\n };\n }\n\n return Store as unknown as StoreConstructor<any, any>;\n}\n\nexport type { LazyObservableArray };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-util.d.mts","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"mappings":";;;UAEiB,iBAAA,WAA4B,UAAA;EAC3C,YAAA,GAAe,UAAA,CAAW,CAAA;EAC1B,UAAA;EACA,YAAA;AAAA;AAAA,KAGU,UAAA,IAAc,MAAA,EAAQ,WAAA,KAAgB,IAAA,YAAgB,
|
|
1
|
+
{"version":3,"file":"react-util.d.mts","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"mappings":";;;UAEiB,iBAAA,WAA4B,UAAA;EAC3C,YAAA,GAAe,UAAA,CAAW,CAAA;EAC1B,UAAA;EACA,YAAA;AAAA;AAAA,KAGU,UAAA,IAAc,MAAA,EAAQ,WAAA,KAAgB,IAAA,YAAgB,OAAO;AAAA,KAC7D,aAAA,gBAA6B,IAAA,yBACjC,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,KAAA,UACrB,OAAA,CAAQ,KAAA;AAAA,UAED,mBAAA;EACf,GAAA,EAAK,aAAA,CAAc,KAAA,EAAO,IAAA;AAAA;AAAA,UAGX,sBAAA,sBAA4C,mBAAA,CAAoB,KAAA,EAAO,IAAA;EACtF,OAAA;EACA,KAAA,GAAQ,KAAA;EACR,KAAA,GAAQ,KAAA;AAAA;AAAA,UAGO,oBAAA,sBAA0C,mBAAA,CAAoB,KAAA,EAAO,IAAA;EACpF,OAAA;EACA,KAAA,EAAO,KAAA;EACP,KAAA;AAAA;AAAA,UAGe,uBAAA,sBAA6C,mBAAA,CAAoB,KAAA,EAAO,IAAA;EACvF,OAAA;EACA,KAAA;EACA,KAAA,EAAO,KAAA;AAAA;AAAA,KAGJ,UAAA,WAAqB,UAAA,IAAc,OAAA,CAAQ,UAAA,CAAW,CAAA;AAAA,KACtD,SAAA,WAAoB,UAAA,IAAc,UAAA,CAAW,CAAA,iCAAkC,IAAA;AAAA,KAExE,eAAA,gBACR,sBAAA,CAAuB,KAAA,EAAO,IAAA,IAC9B,oBAAA,CAAqB,KAAA,EAAO,IAAA,IAC5B,uBAAA,CAAwB,KAAA,EAAO,IAAA;AAAA,cAEtB,UAAA,aAAwB,UAAA,UAAoB,UAAA,CAAW,CAAA,UAAW,SAAA,CAAU,CAAA,GAAE,EAAA,EACrF,CAAA,EAAC,IAAA,GACC,cAAA,EAAc,OAAA,GACV,iBAAA,CAAkB,CAAA,MAC3B,eAAA,CAAgB,KAAA,EAAO,IAAA;;;cC5Cb,QAAA,aAAsB,UAAA,EAAU,EAAA,EACvC,CAAA,EAAC,IAAA,GACC,cAAA,EAAc,OAAA,GACV,iBAAA,CAAkB,CAAA;EAAO,cAAA;AAAA,MAA0B,eAAA,CAAA,OAAA,CAAA,UAAA,CAAA,CAAA,IAAA,UAAA,CAAA,CAAA,iCAAA,IAAA;;;UCJ9C,2BAAA;EACf,OAAA;EACA,OAAO;AAAA;AAAA,iBAGO,oBAAA,eAAmC,IAAA,eACjD,QAAA,EAAU,CAAA,EACV,IAAA,EAAM,cAAA,EACN,OAAA,GAAU,2BAAA,GA0CE,CAAA;;;iBCjDE,kBAAA,CACd,QAAA,EAAU,cAAA,EACV,IAAA,EAAM,cAAA,EACN,OAAA,GAAU,2BAAA;;;cCJC,cAAA,GAAc,QAAA,EAAc,KAAA,CAAM,cAAc;;;cCAhD,eAAA;;;cCAA,iBAAA,GAAiB,GAAA,EACvB,KAAA,CAAM,gBAAgB,CAAC,WAAA,UAAmB,QAAA,GACpC,KAAA,UAAe,MAAA"}
|
package/dist/react-util.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-util.mjs","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"sourcesContent":["import { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAsyncFnOptions<F extends UseAsyncFn> {\n initialValue?: InferValue<F>;\n debounceMs?: number;\n debounceType?: \"leading\" | \"trailing\";\n}\n\nexport type UseAsyncFn = (signal: AbortSignal, ...args: any[]) => Promise<any>;\nexport type UseAsyncFnRun<Value, Args> = Args extends unknown[]\n ? (...args: Args) => Promise<Value>\n : () => Promise<Value>;\n\nexport interface UseAsyncFnStateBase<Value, Args> {\n run: UseAsyncFnRun<Value, Args>;\n}\n\nexport interface UseAsyncFnStateLoading<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: true;\n error?: Error | undefined;\n value?: Value;\n}\n\nexport interface UseAsyncFnStateError<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error: Error;\n value?: undefined;\n}\n\nexport interface UseAsyncFnStateResolved<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error?: undefined;\n value: Value;\n}\n\ntype InferValue<F extends UseAsyncFn> = Awaited<ReturnType<F>>;\ntype InferArgs<F extends UseAsyncFn> = Parameters<F> extends [any, ...infer Rest] ? Rest : [];\n\nexport type UseAsyncFnState<Value, Args> =\n | UseAsyncFnStateLoading<Value, Args>\n | UseAsyncFnStateError<Value, Args>\n | UseAsyncFnStateResolved<Value, Args>;\n\nexport const useAsyncFn = <F extends UseAsyncFn, Value = InferValue<F>, Args = InferArgs<F>>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F>,\n): UseAsyncFnState<Value, Args> => {\n const { debounceType = \"leading\", debounceMs = 650 } = options ?? {};\n\n const timeoutRef = useRef<number | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n\n const [state, setState] = useState<Omit<UseAsyncFnState<Value, Args>, \"run\">>(\n options?.initialValue ? { loading: false, value: options?.initialValue } : { loading: true },\n );\n\n const run = useCallback((...args: Parameters<F>) => {\n window.clearTimeout(timeoutRef.current);\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n if (!state?.loading) {\n setState((prevState) => ({ ...prevState, loading: true }));\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = debounceType === \"leading\" && !timeoutRef.current ? 0 : debounceMs;\n\n timeoutRef.current = window.setTimeout(() => {\n fn(abortController.signal, ...args)\n .then(\n (value) => {\n if (!abortController.signal.aborted) {\n setState({ value, loading: false });\n }\n resolve(value);\n },\n (error) => {\n if (!abortController.signal.aborted) {\n setState({ error, loading: false });\n }\n reject(error);\n },\n )\n .finally(() => {\n timeoutRef.current = undefined;\n });\n }, timeoutMs);\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: deps are controlled by caller\n }, deps);\n\n useEffect(() => {\n return () => abortControllerRef.current?.abort();\n }, []);\n\n return { ...state, run } as UseAsyncFnState<Value, Args>;\n};\n","import { type DependencyList, useEffect, useRef } from \"react\";\nimport { type UseAsyncFn, type UseAsyncFnOptions, useAsyncFn } from \"./useAsyncFn\";\n\nexport const useAsync = <F extends UseAsyncFn>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F> & { runImmediately?: boolean },\n) => {\n const firstRun = useRef(true);\n\n const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};\n\n const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: option changes should not trigger effect\n useEffect(() => {\n const isFirstRun = firstRun.current;\n firstRun.current = false;\n\n if (!runImmediately && isFirstRun) return;\n\n // TODO: how should errors be handled? this isn't great...\n void asyncFn.run();\n }, [asyncFn.run]);\n\n return asyncFn;\n};\n","import { type DependencyList, useCallback, useEffect, useRef } from \"react\";\n\nexport interface UseDebouncedCallbackOptions {\n leading?: boolean;\n delayMs?: number;\n}\n\nexport function useDebouncedCallback<T extends (...args: any) => any>(\n callback: T,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const ref = useRef<\n UseDebouncedCallbackOptions & {\n timeout: number | null;\n mounted: boolean;\n leadingTriggered: boolean;\n }\n >({\n ...options,\n timeout: null,\n mounted: false,\n leadingTriggered: options?.leading ?? false,\n });\n\n useEffect(() => {\n const currentRef = ref.current;\n currentRef.mounted = true;\n currentRef.leadingTriggered = false;\n return () => {\n currentRef.mounted = false;\n window.clearTimeout(currentRef.timeout!);\n };\n }, []);\n\n return useCallback((...params: Parameters<T>) => {\n const currentRef = ref.current;\n window.clearTimeout(currentRef.timeout!);\n if (!currentRef.leadingTriggered && currentRef.leading) {\n currentRef.leadingTriggered = true;\n callback(...params);\n currentRef.timeout = window.setTimeout(() => {\n currentRef.leadingTriggered = true;\n }, currentRef.delayMs ?? 1600);\n } else {\n currentRef.timeout = window.setTimeout(() => {\n if (!currentRef.mounted) return;\n callback(...params);\n currentRef.leadingTriggered = false;\n }, currentRef.delayMs ?? 1600);\n }\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n }, deps) as T;\n}\n","import { type DependencyList, type EffectCallback, useEffect } from \"react\";\nimport { type UseDebouncedCallbackOptions, useDebouncedCallback } from \"./useDebouncedCallback\";\n\nexport function useDebouncedEffect(\n callback: EffectCallback,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const debouncedCallback = useDebouncedCallback(callback, deps, { leading: true, ...options });\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n return useEffect(debouncedCallback, deps);\n}\n","import { useEffect } from \"react\";\n\nexport const useMountEffect = (effectFn: React.EffectCallback): void => useEffect(effectFn, []);\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport const useMountedState = (): (() => boolean) => {\n const mountedRef = useRef<boolean>(false);\n const get = useCallback(() => mountedRef.current, []);\n\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n return get;\n};\n","import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nexport const useResizeObserver = (\n ref: React.MutableRefObject<HTMLElement | null>,\n onResize: (width: number, height: number) => void,\n): void => {\n const onResizeRef = useRef(onResize);\n const handleResize = useCallback(() => {\n if (ref.current) {\n onResizeRef.current(ref.current.clientWidth, ref.current.clientHeight);\n }\n }, [ref]);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n handleResize();\n\n if (typeof ResizeObserver === \"function\") {\n const resizeObserver = new ResizeObserver(() => handleResize());\n\n resizeObserver.observe(el);\n\n return () => resizeObserver.disconnect();\n } else {\n window.addEventListener(\"resize\", handleResize);\n\n return () => window.window.removeEventListener(\"resize\", handleResize);\n }\n }, [ref, handleResize]);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react-util.mjs","names":[],"sources":["../src/react-util/useAsyncFn.ts","../src/react-util/useAsync.ts","../src/react-util/useDebouncedCallback.tsx","../src/react-util/useDebouncedEffect.tsx","../src/react-util/useMountEffect.tsx","../src/react-util/useMountedState.ts","../src/react-util/useResizeObserver.tsx"],"sourcesContent":["import { type DependencyList, useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAsyncFnOptions<F extends UseAsyncFn> {\n initialValue?: InferValue<F>;\n debounceMs?: number;\n debounceType?: \"leading\" | \"trailing\";\n}\n\nexport type UseAsyncFn = (signal: AbortSignal, ...args: any[]) => Promise<any>;\nexport type UseAsyncFnRun<Value, Args> = Args extends unknown[]\n ? (...args: Args) => Promise<Value>\n : () => Promise<Value>;\n\nexport interface UseAsyncFnStateBase<Value, Args> {\n run: UseAsyncFnRun<Value, Args>;\n}\n\nexport interface UseAsyncFnStateLoading<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: true;\n error?: Error | undefined;\n value?: Value;\n}\n\nexport interface UseAsyncFnStateError<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error: Error;\n value?: undefined;\n}\n\nexport interface UseAsyncFnStateResolved<Value, Args> extends UseAsyncFnStateBase<Value, Args> {\n loading: false;\n error?: undefined;\n value: Value;\n}\n\ntype InferValue<F extends UseAsyncFn> = Awaited<ReturnType<F>>;\ntype InferArgs<F extends UseAsyncFn> = Parameters<F> extends [any, ...infer Rest] ? Rest : [];\n\nexport type UseAsyncFnState<Value, Args> =\n | UseAsyncFnStateLoading<Value, Args>\n | UseAsyncFnStateError<Value, Args>\n | UseAsyncFnStateResolved<Value, Args>;\n\nexport const useAsyncFn = <F extends UseAsyncFn, Value = InferValue<F>, Args = InferArgs<F>>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F>,\n): UseAsyncFnState<Value, Args> => {\n const { debounceType = \"leading\", debounceMs = 650 } = options ?? {};\n\n const timeoutRef = useRef<number | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n\n const [state, setState] = useState<Omit<UseAsyncFnState<Value, Args>, \"run\">>(\n options?.initialValue ? { loading: false, value: options?.initialValue } : { loading: true },\n );\n\n const run = useCallback((...args: Parameters<F>) => {\n window.clearTimeout(timeoutRef.current);\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n if (!state?.loading) {\n setState((prevState) => ({ ...prevState, loading: true }));\n }\n\n return new Promise((resolve, reject) => {\n const timeoutMs = debounceType === \"leading\" && !timeoutRef.current ? 0 : debounceMs;\n\n timeoutRef.current = window.setTimeout(() => {\n fn(abortController.signal, ...args)\n .then(\n (value) => {\n if (!abortController.signal.aborted) {\n setState({ value, loading: false });\n }\n resolve(value);\n },\n (error) => {\n if (!abortController.signal.aborted) {\n setState({ error, loading: false });\n }\n reject(error);\n },\n )\n .finally(() => {\n timeoutRef.current = undefined;\n });\n }, timeoutMs);\n });\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: deps are controlled by caller\n }, deps);\n\n useEffect(() => {\n return () => abortControllerRef.current?.abort();\n }, []);\n\n return { ...state, run } as UseAsyncFnState<Value, Args>;\n};\n","import { type DependencyList, useEffect, useRef } from \"react\";\nimport { type UseAsyncFn, type UseAsyncFnOptions, useAsyncFn } from \"./useAsyncFn\";\n\nexport const useAsync = <F extends UseAsyncFn>(\n fn: F,\n deps: DependencyList = [],\n options?: UseAsyncFnOptions<F> & { runImmediately?: boolean },\n) => {\n const firstRun = useRef(true);\n\n const { runImmediately = true, ...useAsyncFnOptions } = options ?? {};\n\n const asyncFn = useAsyncFn(fn, deps, useAsyncFnOptions);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: option changes should not trigger effect\n useEffect(() => {\n const isFirstRun = firstRun.current;\n firstRun.current = false;\n\n if (!runImmediately && isFirstRun) return;\n\n // TODO: how should errors be handled? this isn't great...\n void asyncFn.run();\n }, [asyncFn.run]);\n\n return asyncFn;\n};\n","import { type DependencyList, useCallback, useEffect, useRef } from \"react\";\n\nexport interface UseDebouncedCallbackOptions {\n leading?: boolean;\n delayMs?: number;\n}\n\nexport function useDebouncedCallback<T extends (...args: any) => any>(\n callback: T,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const ref = useRef<\n UseDebouncedCallbackOptions & {\n timeout: number | null;\n mounted: boolean;\n leadingTriggered: boolean;\n }\n >({\n ...options,\n timeout: null,\n mounted: false,\n leadingTriggered: options?.leading ?? false,\n });\n\n useEffect(() => {\n const currentRef = ref.current;\n currentRef.mounted = true;\n currentRef.leadingTriggered = false;\n return () => {\n currentRef.mounted = false;\n window.clearTimeout(currentRef.timeout!);\n };\n }, []);\n\n return useCallback((...params: Parameters<T>) => {\n const currentRef = ref.current;\n window.clearTimeout(currentRef.timeout!);\n if (!currentRef.leadingTriggered && currentRef.leading) {\n currentRef.leadingTriggered = true;\n callback(...params);\n currentRef.timeout = window.setTimeout(() => {\n currentRef.leadingTriggered = true;\n }, currentRef.delayMs ?? 1600);\n } else {\n currentRef.timeout = window.setTimeout(() => {\n if (!currentRef.mounted) return;\n callback(...params);\n currentRef.leadingTriggered = false;\n }, currentRef.delayMs ?? 1600);\n }\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n }, deps) as T;\n}\n","import { type DependencyList, type EffectCallback, useEffect } from \"react\";\nimport { type UseDebouncedCallbackOptions, useDebouncedCallback } from \"./useDebouncedCallback\";\n\nexport function useDebouncedEffect(\n callback: EffectCallback,\n deps: DependencyList,\n options?: UseDebouncedCallbackOptions,\n) {\n const debouncedCallback = useDebouncedCallback(callback, deps, { leading: true, ...options });\n // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>\n return useEffect(debouncedCallback, deps);\n}\n","import { useEffect } from \"react\";\n\nexport const useMountEffect = (effectFn: React.EffectCallback): void => useEffect(effectFn, []);\n","import { useCallback, useEffect, useRef } from \"react\";\n\nexport const useMountedState = (): (() => boolean) => {\n const mountedRef = useRef<boolean>(false);\n const get = useCallback(() => mountedRef.current, []);\n\n useEffect(() => {\n mountedRef.current = true;\n\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n return get;\n};\n","import { useCallback, useLayoutEffect, useRef } from \"react\";\n\nexport const useResizeObserver = (\n ref: React.MutableRefObject<HTMLElement | null>,\n onResize: (width: number, height: number) => void,\n): void => {\n const onResizeRef = useRef(onResize);\n const handleResize = useCallback(() => {\n if (ref.current) {\n onResizeRef.current(ref.current.clientWidth, ref.current.clientHeight);\n }\n }, [ref]);\n\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) {\n return;\n }\n\n handleResize();\n\n if (typeof ResizeObserver === \"function\") {\n const resizeObserver = new ResizeObserver(() => handleResize());\n\n resizeObserver.observe(el);\n\n return () => resizeObserver.disconnect();\n } else {\n window.addEventListener(\"resize\", handleResize);\n\n return () => window.window.removeEventListener(\"resize\", handleResize);\n }\n }, [ref, handleResize]);\n};\n"],"mappings":"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"}
|
package/dist/router.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import * as _$react from "react";
|
|
2
1
|
import React$1 from "react";
|
|
3
|
-
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
2
|
import { History, Location } from "history";
|
|
5
3
|
|
|
6
4
|
//#region src/router/outlet.d.ts
|
|
@@ -141,9 +139,10 @@ type ExtractParams<P> = P extends `${infer S1}/${infer Rest}` ? ExtractParam<S1,
|
|
|
141
139
|
type ExtractPaths<R> = { [S in keyof R]: S extends string ? S extends "index" ? "/" : R[S] extends Leaf ? `/${S}` : JoinSegments<S, ExtractPaths<R[S]>> : never }[keyof R];
|
|
142
140
|
//#endregion
|
|
143
141
|
//#region src/router/components/link.d.ts
|
|
144
|
-
type LinkComponentProps<C extends React$1.ElementType> = Omit<React$1.ComponentProps<C>, "
|
|
142
|
+
type LinkComponentProps<C extends React$1.ElementType> = Omit<React$1.ComponentProps<C>, "ref" | "exact" | "to" | "params" | "onClick" | "asChild">;
|
|
145
143
|
type LinkPropsBase<C extends React$1.ElementType, I extends React$1.ElementType = C> = LinkComponentProps<C> & {
|
|
146
144
|
exact?: boolean;
|
|
145
|
+
preserveSearch?: boolean;
|
|
147
146
|
ref?: React$1.Ref<React$1.ComponentRef<I>>;
|
|
148
147
|
};
|
|
149
148
|
interface LinkComponent<C extends React$1.ElementType, I extends React$1.ElementType = C> {
|
|
@@ -194,14 +193,14 @@ declare const RouterOutlet: React.FC<{
|
|
|
194
193
|
route: Route;
|
|
195
194
|
components: (Component | undefined)[];
|
|
196
195
|
}>;
|
|
197
|
-
declare const routerContext:
|
|
196
|
+
declare const routerContext: import("react").Context<RouterStore>;
|
|
198
197
|
declare const useRouter: () => RouterStore;
|
|
199
198
|
interface RouterProps {
|
|
200
199
|
store: RouterStore;
|
|
201
200
|
}
|
|
202
201
|
declare const Router: (({
|
|
203
202
|
store
|
|
204
|
-
}: RouterProps) =>
|
|
203
|
+
}: RouterProps) => import("react/jsx-runtime").JSX.Element | null) & {
|
|
205
204
|
displayName: string;
|
|
206
205
|
};
|
|
207
206
|
//#endregion
|
package/dist/router.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.mts","names":[],"sources":["../src/router/outlet.tsx","../src/router/route.tsx","../src/router/symbols.ts","../src/router/types.ts","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/router.store.ts","../src/router/components/router.tsx","../src/router/make-routes.tsx","../src/router/redirect.ts","../src/router/util.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"router.d.mts","names":[],"sources":["../src/router/outlet.tsx","../src/router/route.tsx","../src/router/symbols.ts","../src/router/types.ts","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/router.store.ts","../src/router/components/router.tsx","../src/router/make-routes.tsx","../src/router/redirect.ts","../src/router/util.ts"],"mappings":"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"}
|
package/dist/router.mjs
CHANGED
|
@@ -76,7 +76,7 @@ const Router = observer(({ store }) => {
|
|
|
76
76
|
//#endregion
|
|
77
77
|
//#region src/router/components/link.tsx
|
|
78
78
|
const makeLinkComponent = (C, baseProps) => {
|
|
79
|
-
return observer(({ to, params, exact, children, ...props }) => {
|
|
79
|
+
return observer(({ to, params, exact, preserveSearch, children, ...props }) => {
|
|
80
80
|
const router = useRouter();
|
|
81
81
|
const mergedProps = {
|
|
82
82
|
...baseProps,
|
|
@@ -89,6 +89,7 @@ const makeLinkComponent = (C, baseProps) => {
|
|
|
89
89
|
if (props.disabled) return;
|
|
90
90
|
router.navigate({
|
|
91
91
|
to,
|
|
92
|
+
preserveSearch,
|
|
92
93
|
...params
|
|
93
94
|
});
|
|
94
95
|
}, [
|
|
@@ -116,6 +117,7 @@ const Navigate = (props) => {
|
|
|
116
117
|
const DefaultOutlet = ({ children }) => children;
|
|
117
118
|
const LoadingPlaceholder = () => /* @__PURE__ */ jsx("p", { children: "Loading..." });
|
|
118
119
|
var Outlet = class {
|
|
120
|
+
config;
|
|
119
121
|
state = "preloading";
|
|
120
122
|
promise;
|
|
121
123
|
data;
|
|
@@ -183,6 +185,7 @@ var Outlet = class {
|
|
|
183
185
|
//#endregion
|
|
184
186
|
//#region src/router/redirect.ts
|
|
185
187
|
var Redirect = class {
|
|
188
|
+
options;
|
|
186
189
|
constructor(options) {
|
|
187
190
|
this.options = options;
|
|
188
191
|
}
|
package/dist/router.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.mjs","names":[],"sources":["../src/router/symbols.ts","../src/router/util.ts","../src/router/components/router.tsx","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/outlet.tsx","../src/router/redirect.ts","../src/router/route.tsx","../src/router/make-routes.tsx","../src/router/router.store.ts"],"sourcesContent":["export const CONTEXT: unique symbol = Symbol.for(\"MOBX_ROUTER_CONTEXT\");\nexport const LAYOUT: unique symbol = Symbol.for(\"MOBX_ROUTER_LAYOUT\");\nexport const WRAPPER: unique symbol = Symbol.for(\"MOBX_ROUTER_WRAPPER\");\nexport const LOAD: unique symbol = Symbol.for(\"MOBX_ROUTER_LOAD\");\nexport const GUARD: unique symbol = Symbol.for(\"MOBX_ROUTER_GUARD\");\nexport const PAGE: unique symbol = Symbol.for(\"MOBX_ROUTER_PAGE\");\nexport const REDIRECT: unique symbol = Symbol.for(\"MOBX_ROUTER_REDIRECT\");\n","import { PAGE, REDIRECT } from \"./symbols\";\nimport type { Component, LazyComponent, Leaf, Obj, Page, Redirector } from \"./types\";\n\nexport const resolvePath = (to: string, params?: Obj): string => {\n return to.replaceAll(/:[^/]*/g, (segment) => {\n const value = params?.[segment.slice(1)];\n if (!value)\n throw new Error(`Unable to resolve route '${to}. Paramater '${segment}' not specified.`);\n return value;\n });\n};\n\nexport const isComponent = (data: any): data is Component => {\n if (!data) return false;\n return (\n typeof data === \"function\" ||\n (typeof data === \"object\" && data[\"$$typeof\"] === Symbol.for(\"react.memo\"))\n );\n};\n\nexport const isPage = (data: any): data is Page => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(PAGE);\n};\n\nexport const isRedirect = (data: any): data is Redirector => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(REDIRECT);\n};\n\nexport const isLeaf = (data: any): data is Leaf => {\n return isComponent(data) || isPage(data) || isRedirect(data);\n};\n\nexport const isLazyComponent = (data: any): data is LazyComponent => {\n return typeof data === \"function\" && data.toString().startsWith(\"() => import(\");\n};\n","import { observer } from \"mobx-react-lite\";\nimport { createContext, useContext } from \"react\";\nimport type { Route } from \"../route\";\nimport type { RouterStore } from \"../router.store\";\nimport type { Component } from \"../types\";\n\nexport const PassThrough: Component = ({ children }) => children;\n\n// Plain (non-observer) renderer. State observation lives one level up\n// in `Router`, so the page component renders as a child of a plain\n// FunctionComponent — no memo wrapper in the parent chain to interact\n// with React Refresh's family-update propagation.\nexport const RouterOutlet: React.FC<{ route: Route; components: (Component | undefined)[] }> = ({\n route,\n components,\n}) => {\n const [C, ...remaining] = components;\n\n if (!C) return null;\n\n return (\n <C route={route}>\n {remaining.length > 0 && <RouterOutlet route={route} components={remaining} />}\n </C>\n );\n};\n\nexport const routerContext = createContext<RouterStore>(null as any);\nexport const useRouter = () => useContext(routerContext);\n\nexport interface RouterProps {\n store: RouterStore;\n}\n\nexport const Router = observer(({ store }: RouterProps) => {\n if (!store.activeRoute) {\n return null;\n }\n\n const Layout = store.activeRoute.layout ?? PassThrough;\n const components = store.activeRoute.outlets.map((o) => o.Component);\n\n return (\n <routerContext.Provider value={store}>\n <Layout route={store.activeRoute}>\n <RouterOutlet route={store.activeRoute} components={components} />\n </Layout>\n </routerContext.Provider>\n );\n});\n","import { observer } from \"mobx-react-lite\";\nimport React, { useCallback } from \"react\";\nimport type {\n DynamicRoutePath,\n ExtractParams,\n NavigateOptions,\n RoutePath,\n StaticRoutePath,\n} from \"../types\";\nimport { resolvePath } from \"../util\";\nimport { useRouter } from \"./router\";\n\ntype LinkComponentProps<C extends React.ElementType> = Omit<\n React.ComponentProps<C>,\n \" ref\" | \"exact\" | \"to\" | \"params\" | \"onClick\"\n>;\n\nexport type LinkPropsBase<\n C extends React.ElementType,\n I extends React.ElementType = C,\n> = LinkComponentProps<C> & {\n exact?: boolean;\n ref?: React.Ref<React.ComponentRef<I>>;\n};\n\n// function overloading is much faster than leveraging conditional types\n// but once the typescript go compiler is released and performance is no\n// longer an issue, it might make sense to simplify this a bit so it can\n// be more easily consumed by users\nexport interface LinkComponent<C extends React.ElementType, I extends React.ElementType = C> {\n <P extends StaticRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params?: undefined },\n ): React.ReactNode;\n <P extends DynamicRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params: ExtractParams<P> },\n ): React.ReactNode;\n}\n\n// final thing to do is make sure refs still work in React 19\n\n// this smooths over some of the awkwardness when extending this component\nexport const makeLinkComponent = <C extends React.ElementType, I extends React.ElementType = C>(\n C: C,\n baseProps?: Partial<LinkComponentProps<C>> & { as?: I },\n) => {\n return observer(({ to, params, exact, children, ...props }: any) => {\n const router = useRouter();\n const mergedProps = { ...baseProps, ...props };\n\n if (props.role !== \"link\") {\n mergedProps.href = resolvePath(to, params);\n }\n\n if (router.doesPathMatch(to, exact)) {\n mergedProps[\"aria-current\"] = \"page\";\n }\n\n mergedProps.onClick = useCallback(\n (event: React.MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (props.disabled) return;\n router.navigate({ to, ...(params as any) } as NavigateOptions<RoutePath>);\n },\n [router, params, to, props.disabled],\n );\n\n return React.createElement(C, mergedProps, children);\n }) as LinkComponent<C, I>;\n};\n\n//export const Link = makeLinkComponent('a');\n","import { useLayoutEffect } from \"react\";\nimport type { NavigateOptions, RoutePath } from \"../types\";\nimport { useRouter } from \"./router\";\n\nexport const Navigate = <P extends RoutePath>(props: NavigateOptions<P>) => {\n const router = useRouter();\n\n useLayoutEffect(() => {\n router.navigate(props);\n }, [router, props]);\n\n return null;\n};\n","import { makeAutoObservable, observable } from \"mobx\";\nimport type { Route } from \"./route\";\nimport type { Component, LazyComponent, Loader } from \"./types\";\nimport { isLazyComponent } from \"./util\";\n\nexport interface OutletConfig {\n component?: Component | LazyComponent;\n loader?: Loader;\n}\n\nexport type RouteSegmentState = \"preloading\" | \"loading\" | \"error\" | \"ready\";\n\nexport const DefaultOutlet: Component = ({ children }) => children;\n\nconst LoadingPlaceholder: Component = () => <p>Loading...</p>;\n\nexport class Outlet {\n state: RouteSegmentState = \"preloading\";\n promise: Promise<unknown> | undefined;\n data: unknown;\n\n // Plain (non-observable) reference. The page component must reach\n // React unmediated by MobX — once MobX deep-observes the holder,\n // React Refresh can no longer swap the page identity via family\n // lookup on the original function. This mirrors how Route holds\n // `layout` as a plain field under makeObservable.\n component: Component | undefined;\n\n get Component(): Component | undefined {\n switch (this.state) {\n case \"loading\":\n return LoadingPlaceholder;\n case \"ready\":\n return this.component ?? DefaultOutlet;\n default:\n return undefined;\n }\n }\n\n constructor(readonly config: OutletConfig) {\n if (!isLazyComponent(config.component)) {\n this.component = config.component;\n }\n\n makeAutoObservable<Outlet, \"component\" | \"config\">(this, {\n promise: observable.ref,\n data: observable.ref,\n component: false,\n config: false,\n });\n }\n\n async load(route: Route): Promise<void> {\n const promises: Promise<void>[] = [];\n\n if (isLazyComponent(this.config.component) && !this.component) {\n promises.push(this.loadComponent());\n }\n\n if (this.config.loader) {\n promises.push(this.loadData(route));\n }\n\n if (!promises.length) {\n this.setState(\"ready\");\n return;\n }\n\n // wait to transition to loading to avoid\n // screen flashes when the loader function\n // executes quickly\n const preloadingTimer = setTimeout(() => {\n if (this.state === \"preloading\") {\n this.setState(\"loading\");\n }\n }, 300);\n\n this.promise = Promise.all(promises)\n .then(() => {\n clearTimeout(preloadingTimer);\n if (this.state === \"loading\") {\n // if we had to transition to regular loading because\n // the loader was taking too long, force an additional\n // timeout to prevent a loading flash\n setTimeout(() => {\n this.setState(\"ready\");\n }, 300);\n } else {\n this.setState(\"ready\");\n }\n })\n .catch(() => {\n clearTimeout(preloadingTimer);\n // TODO: check for type of error\n // handle access denied and redirects\n this.setState(\"error\");\n });\n\n await this.promise;\n }\n\n setData(data: unknown) {\n this.data = data;\n }\n\n setState(state: RouteSegmentState) {\n this.state = state;\n }\n\n private async loadData(route: Route): Promise<void> {\n await this.config.loader?.(route).then((data) => this.setData(data));\n }\n\n private async loadComponent(): Promise<void> {\n if (!isLazyComponent(this.config.component)) return;\n const module = await this.config.component();\n for (const exportName in module) {\n if (exportName === \"default\" || exportName.endsWith(\"Page\")) {\n this.component = module[exportName];\n return;\n }\n }\n throw new Error(\n \"Lazy route component module did not export `default` or a `*Page` named export\",\n );\n }\n}\n","import type { NavigateOptions, RoutePath } from \"./types\";\n\nexport class Redirect<P extends RoutePath> {\n constructor(readonly options: NavigateOptions<P>) {}\n}\n\nexport const redirect = <P extends RoutePath>(options: NavigateOptions<P>): Redirect<P> =>\n new Redirect(options);\n","import { computed, makeObservable } from \"mobx\";\nimport type { Outlet } from \"./outlet\";\nimport type { Component, Guard, Obj } from \"./types\";\n\nexport interface RouteConfig {\n path: string;\n outlets: Outlet[];\n guards: Guard[];\n context?: Obj;\n layout?: Component;\n params: Obj;\n}\n\nexport class Route {\n readonly path: string;\n readonly outlets: Outlet[];\n readonly guards: Guard[];\n readonly context: Obj;\n readonly params: Obj;\n readonly layout?: Component;\n\n get data(): Obj {\n return Object.assign({}, ...this.outlets.map((o) => o.data));\n }\n\n constructor(def: RouteConfig) {\n this.path = def.path;\n this.guards = def.guards;\n this.context = def.context ?? {};\n this.outlets = def.outlets;\n this.params = def.params;\n this.layout = def.layout;\n\n makeObservable(this, {\n data: computed,\n });\n }\n\n async guard(): Promise<void> {\n for (const guard of this.guards) {\n await guard(this);\n }\n }\n\n async load(): Promise<void> {\n await Promise.all(this.outlets.map((outlet) => outlet.load(this)));\n }\n}\n","import { Outlet } from \"./outlet\";\nimport { Redirect } from \"./redirect\";\nimport { Route } from \"./route\";\nimport { CONTEXT, GUARD, LAYOUT, LOAD, PAGE, REDIRECT, WRAPPER } from \"./symbols\";\nimport type { Component, Guard, Obj, Routes } from \"./types\";\nimport { isComponent, isLazyComponent, isLeaf, isPage, isRedirect } from \"./util\";\n\nconst pathToSegments = (path: string): string[] => {\n return path.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n};\n\nexport interface MatchState {\n segments: string[];\n context: Obj;\n params: Obj;\n outlets: (Outlet | undefined)[];\n guards: (Guard | undefined)[];\n layout?: Component;\n}\n\nexport const makeRoute = (matchState: MatchState): Route => {\n const outlets = matchState.outlets.filter((o) => o !== undefined);\n const guards = matchState.guards.filter((g) => g !== undefined);\n\n return new Route({ ...matchState, outlets, guards, path: matchState.segments.join(\"/\") });\n};\n\nexport const matchRoute = (path: string, routeDef: Routes, matchState?: MatchState): Route => {\n const state: MatchState = {\n segments: [],\n params: {},\n ...matchState,\n layout: routeDef[LAYOUT] ?? matchState?.layout,\n context: { ...matchState?.context, ...routeDef[CONTEXT] },\n guards: [...(matchState?.guards ?? []), routeDef[GUARD]],\n outlets: [\n ...(matchState?.outlets ?? []),\n routeDef[WRAPPER] ? new Outlet({ component: routeDef[WRAPPER] }) : undefined,\n routeDef[LOAD] ? new Outlet({ loader: routeDef[LOAD] }) : undefined,\n ],\n };\n\n const [segment, ...remainingSegments] = pathToSegments(path);\n const remainingPath = remainingSegments.join(\"/\");\n\n let defAtSegment = routeDef[segment || \"index\"];\n\n if (!defAtSegment) {\n const matchedSegment = Object.keys(routeDef).find((segment) => segment.startsWith(\"$\"));\n if (matchedSegment) {\n defAtSegment = routeDef[matchedSegment];\n state.params[matchedSegment.slice(1)] = segment;\n }\n }\n\n if (!defAtSegment) {\n throw new Error(\"Not Found.\");\n }\n\n state.segments.push(segment ?? \"index\");\n\n if (isLeaf(defAtSegment)) {\n if (remainingPath) {\n throw new Error(\"Not Found.\");\n }\n\n if (isRedirect(defAtSegment)) {\n const redirect =\n typeof defAtSegment[REDIRECT] === \"string\"\n ? { to: defAtSegment[REDIRECT] }\n : defAtSegment[REDIRECT];\n throw new Redirect(redirect as any);\n }\n\n if (isComponent(defAtSegment) || isLazyComponent(defAtSegment)) {\n state.outlets.push(new Outlet({ component: defAtSegment }));\n return makeRoute(state);\n }\n }\n\n // at this point we have a nested route or a [Page] definition\n\n if (isPage(defAtSegment)) {\n state.layout = defAtSegment[LAYOUT] ?? state.layout;\n Object.assign(state.context, defAtSegment[CONTEXT]);\n state.guards.push(defAtSegment[GUARD]);\n state.outlets.push(\n new Outlet({\n component: defAtSegment[PAGE],\n loader: defAtSegment[LOAD],\n }),\n );\n\n return makeRoute(state);\n }\n\n // now we know we have a nested route\n return matchRoute(remainingPath, defAtSegment, state);\n};\n\n// TODO: ideally this could resolve to something less than R,\n// but specific enough to infer all paths as a literal union.\n// As it stands, there are certain things we can't access reliably\n// without the compiler complaining about circular references\n// try \"as const satisfies\" approach which would allow us to\n// exchange a less specific version of MobxRoutesRoot for this\nexport const makeRoutes =\n () =>\n <R extends Routes>(routes: R): R => {\n // todo: perform some validation here\n // - no forward slashes in keys\n // - at most one variable segment per level\n // - only lowercase letters (except variables)\n // - paths/variables cannot contain $ that aren't at the beginning\n // - path variables must be unique across a path\n\n return routes;\n };\n","import { createBrowserHistory, type History, type Location } from \"history\";\nimport { action, computed, makeObservable, observable, runInAction } from \"mobx\";\nimport { matchRoute } from \"./make-routes\";\nimport { Redirect } from \"./redirect\";\nimport type { Route } from \"./route\";\nimport type { Component, MobxRouterConfig, NavigateOptions, Obj, RoutePath, Routes } from \"./types\";\nimport { resolvePath } from \"./util\";\n\nexport interface MobxRenderSegment {\n segment: string;\n component: Component;\n props?: Obj;\n}\n\nexport class RouterStore {\n readonly history: History;\n\n routesDef?: Routes;\n\n location!: Location;\n activeRoute: Route | undefined;\n\n get search(): URLSearchParams {\n return new URLSearchParams(this.location?.search);\n }\n\n get query(): Record<string, string> {\n return Object.fromEntries(this.search);\n }\n\n get pathParams(): Record<string, string> {\n const params = {} as Record<string, string>;\n\n const paramSegments = this.activeSegments.filter((segment) => segment.startsWith(\"$\"));\n const pathValues = this.location.pathname.split(\"/\").slice(1);\n\n paramSegments.forEach((segment, index) => {\n const value = pathValues[index + 1];\n if (value) {\n params[segment] = value;\n }\n });\n\n return params as Record<string, string>;\n }\n\n get activeSegments(): string[] {\n return this.activeRoute?.path.split(\"/\") ?? [];\n }\n\n constructor(config?: MobxRouterConfig) {\n makeObservable(this, {\n location: observable.ref,\n activeRoute: observable.ref,\n\n search: computed,\n pathParams: computed,\n activeSegments: computed,\n\n setLocation: action,\n });\n\n this.history = config?.history ?? createBrowserHistory();\n }\n\n initialize(routesDef: Routes): void {\n this.routesDef = routesDef;\n this.history.listen((data) => {\n void this.setLocation(data.location);\n });\n\n void this.setLocation(this.history.location);\n }\n\n doesPathMatch<P extends RoutePath>(path: P, exact?: boolean): boolean {\n const segments = path.slice(1).split(\"/\");\n const segmentsMatch = segments.every(\n (segment, i) => segment === this.activeSegments[i] || segment.startsWith(\"$\"),\n );\n\n return (\n segmentsMatch &&\n this.activeSegments.length >= segments.length &&\n (!exact || segments.length === this.activeSegments.length)\n );\n }\n\n navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n if (!document.startViewTransition) {\n this._navigate(options);\n } else {\n const transition = document.startViewTransition(() => this._navigate(options));\n transition.ready.catch((e) => console.log(e, typeof e));\n }\n }\n\n _navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n const { to, replace, state, search = {}, preserveSearch, params } = options;\n\n const searchParams = search instanceof URLSearchParams ? search : new URLSearchParams(search);\n\n if (preserveSearch) {\n for (const [name, value] of this.search) {\n if (!searchParams.has(name)) {\n searchParams.set(name, value);\n }\n }\n }\n\n const location = {\n pathname: resolvePath(to, params),\n search: searchParams.size ? `?${searchParams.toString()}` : undefined,\n };\n\n if (replace) {\n this.history.replace(location, state);\n } else {\n this.history.push(location, state);\n }\n }\n\n setQueryParam(param: string, value: string): void {\n const params = new URLSearchParams(this.location.search);\n params.set(param, value);\n this.history.replace({ search: params.toString() });\n }\n\n removeQueryParam(param: string): string | undefined {\n const params = new URLSearchParams(this.location.search);\n const value = params.get(param) ?? undefined;\n if (value !== undefined) {\n params.delete(param);\n this.history.replace({ search: params.toString() });\n }\n return value;\n }\n\n async setLocation(location: Location): Promise<void> {\n if (!this.routesDef) return;\n\n // TODO: this should not be the responsibility of mobx-router\n // and should really be handled server-side\n if (location.pathname !== \"/\" && location.pathname.endsWith(\"/\")) {\n this.history.replace({ ...location, pathname: location.pathname.slice(0, -1) });\n return;\n }\n\n this.location = location;\n\n try {\n const matchedRoute = matchRoute(location.pathname, this.routesDef);\n\n await matchedRoute.guard();\n\n // navigating within a guard function\n // is essentially a redirect\n if (this.location !== location) {\n return;\n }\n\n runInAction(() => {\n this.activeRoute = matchedRoute;\n });\n\n await this.activeRoute?.load();\n } catch (e) {\n if (e instanceof Redirect) {\n this.navigate(e.options);\n return;\n }\n throw e;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.mjs","names":[],"sources":["../src/router/symbols.ts","../src/router/util.ts","../src/router/components/router.tsx","../src/router/components/link.tsx","../src/router/components/navigate.tsx","../src/router/outlet.tsx","../src/router/redirect.ts","../src/router/route.tsx","../src/router/make-routes.tsx","../src/router/router.store.ts"],"sourcesContent":["export const CONTEXT: unique symbol = Symbol.for(\"MOBX_ROUTER_CONTEXT\");\nexport const LAYOUT: unique symbol = Symbol.for(\"MOBX_ROUTER_LAYOUT\");\nexport const WRAPPER: unique symbol = Symbol.for(\"MOBX_ROUTER_WRAPPER\");\nexport const LOAD: unique symbol = Symbol.for(\"MOBX_ROUTER_LOAD\");\nexport const GUARD: unique symbol = Symbol.for(\"MOBX_ROUTER_GUARD\");\nexport const PAGE: unique symbol = Symbol.for(\"MOBX_ROUTER_PAGE\");\nexport const REDIRECT: unique symbol = Symbol.for(\"MOBX_ROUTER_REDIRECT\");\n","import { PAGE, REDIRECT } from \"./symbols\";\nimport type { Component, LazyComponent, Leaf, Obj, Page, Redirector } from \"./types\";\n\nexport const resolvePath = (to: string, params?: Obj): string => {\n return to.replaceAll(/:[^/]*/g, (segment) => {\n const value = params?.[segment.slice(1)];\n if (!value)\n throw new Error(`Unable to resolve route '${to}. Paramater '${segment}' not specified.`);\n return value;\n });\n};\n\nexport const isComponent = (data: any): data is Component => {\n if (!data) return false;\n return (\n typeof data === \"function\" ||\n (typeof data === \"object\" && data[\"$$typeof\"] === Symbol.for(\"react.memo\"))\n );\n};\n\nexport const isPage = (data: any): data is Page => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(PAGE);\n};\n\nexport const isRedirect = (data: any): data is Redirector => {\n if (typeof data !== \"object\") return false;\n const symbols = Object.getOwnPropertySymbols(data);\n return symbols.includes(REDIRECT);\n};\n\nexport const isLeaf = (data: any): data is Leaf => {\n return isComponent(data) || isPage(data) || isRedirect(data);\n};\n\nexport const isLazyComponent = (data: any): data is LazyComponent => {\n return typeof data === \"function\" && data.toString().startsWith(\"() => import(\");\n};\n","import { observer } from \"mobx-react-lite\";\nimport { createContext, useContext } from \"react\";\nimport type { Route } from \"../route\";\nimport type { RouterStore } from \"../router.store\";\nimport type { Component } from \"../types\";\n\nexport const PassThrough: Component = ({ children }) => children;\n\n// Plain (non-observer) renderer. State observation lives one level up\n// in `Router`, so the page component renders as a child of a plain\n// FunctionComponent — no memo wrapper in the parent chain to interact\n// with React Refresh's family-update propagation.\nexport const RouterOutlet: React.FC<{ route: Route; components: (Component | undefined)[] }> = ({\n route,\n components,\n}) => {\n const [C, ...remaining] = components;\n\n if (!C) return null;\n\n return (\n <C route={route}>\n {remaining.length > 0 && <RouterOutlet route={route} components={remaining} />}\n </C>\n );\n};\n\nexport const routerContext = createContext<RouterStore>(null as any);\nexport const useRouter = () => useContext(routerContext);\n\nexport interface RouterProps {\n store: RouterStore;\n}\n\nexport const Router = observer(({ store }: RouterProps) => {\n if (!store.activeRoute) {\n return null;\n }\n\n const Layout = store.activeRoute.layout ?? PassThrough;\n const components = store.activeRoute.outlets.map((o) => o.Component);\n\n return (\n <routerContext.Provider value={store}>\n <Layout route={store.activeRoute}>\n <RouterOutlet route={store.activeRoute} components={components} />\n </Layout>\n </routerContext.Provider>\n );\n});\n","import { observer } from \"mobx-react-lite\";\nimport React, { useCallback } from \"react\";\nimport type {\n DynamicRoutePath,\n ExtractParams,\n NavigateOptions,\n RoutePath,\n StaticRoutePath,\n} from \"../types\";\nimport { resolvePath } from \"../util\";\nimport { useRouter } from \"./router\";\n\ntype LinkComponentProps<C extends React.ElementType> = Omit<\n React.ComponentProps<C>,\n \"ref\" | \"exact\" | \"to\" | \"params\" | \"onClick\" | \"asChild\"\n>;\n\nexport type LinkPropsBase<\n C extends React.ElementType,\n I extends React.ElementType = C,\n> = LinkComponentProps<C> & {\n exact?: boolean;\n preserveSearch?: boolean;\n ref?: React.Ref<React.ComponentRef<I>>;\n};\n\n// function overloading is much faster than leveraging conditional types\n// but once the typescript go compiler is released and performance is no\n// longer an issue, it might make sense to simplify this a bit so it can\n// be more easily consumed by users\nexport interface LinkComponent<C extends React.ElementType, I extends React.ElementType = C> {\n <P extends StaticRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params?: undefined },\n ): React.ReactNode;\n <P extends DynamicRoutePath>(\n props: LinkPropsBase<C, I> & { to: P; params: ExtractParams<P> },\n ): React.ReactNode;\n}\n\n// final thing to do is make sure refs still work in React 19\n\n// this smooths over some of the awkwardness when extending this component\nexport const makeLinkComponent = <C extends React.ElementType, I extends React.ElementType = C>(\n C: C,\n baseProps?: Partial<LinkComponentProps<C>> & { as?: I },\n) => {\n return observer(({ to, params, exact, preserveSearch, children, ...props }: any) => {\n const router = useRouter();\n const mergedProps = { ...baseProps, ...props };\n\n if (props.role !== \"link\") {\n mergedProps.href = resolvePath(to, params);\n }\n\n if (router.doesPathMatch(to, exact)) {\n mergedProps[\"aria-current\"] = \"page\";\n }\n\n mergedProps.onClick = useCallback(\n (event: React.MouseEvent<HTMLElement>) => {\n event.preventDefault();\n if (props.disabled) return;\n router.navigate({ to, preserveSearch, ...(params as any) } as NavigateOptions<RoutePath>);\n },\n [router, params, to, props.disabled],\n );\n\n return React.createElement(C, mergedProps, children);\n }) as LinkComponent<C, I>;\n};\n\n//export const Link = makeLinkComponent('a');\n","import { useLayoutEffect } from \"react\";\nimport type { NavigateOptions, RoutePath } from \"../types\";\nimport { useRouter } from \"./router\";\n\nexport const Navigate = <P extends RoutePath>(props: NavigateOptions<P>) => {\n const router = useRouter();\n\n useLayoutEffect(() => {\n router.navigate(props);\n }, [router, props]);\n\n return null;\n};\n","import { makeAutoObservable, observable } from \"mobx\";\nimport type { Route } from \"./route\";\nimport type { Component, LazyComponent, Loader } from \"./types\";\nimport { isLazyComponent } from \"./util\";\n\nexport interface OutletConfig {\n component?: Component | LazyComponent;\n loader?: Loader;\n}\n\nexport type RouteSegmentState = \"preloading\" | \"loading\" | \"error\" | \"ready\";\n\nexport const DefaultOutlet: Component = ({ children }) => children;\n\nconst LoadingPlaceholder: Component = () => <p>Loading...</p>;\n\nexport class Outlet {\n state: RouteSegmentState = \"preloading\";\n promise: Promise<unknown> | undefined;\n data: unknown;\n\n // Plain (non-observable) reference. The page component must reach\n // React unmediated by MobX — once MobX deep-observes the holder,\n // React Refresh can no longer swap the page identity via family\n // lookup on the original function. This mirrors how Route holds\n // `layout` as a plain field under makeObservable.\n component: Component | undefined;\n\n get Component(): Component | undefined {\n switch (this.state) {\n case \"loading\":\n return LoadingPlaceholder;\n case \"ready\":\n return this.component ?? DefaultOutlet;\n default:\n return undefined;\n }\n }\n\n constructor(readonly config: OutletConfig) {\n if (!isLazyComponent(config.component)) {\n this.component = config.component;\n }\n\n makeAutoObservable<Outlet, \"component\" | \"config\">(this, {\n promise: observable.ref,\n data: observable.ref,\n component: false,\n config: false,\n });\n }\n\n async load(route: Route): Promise<void> {\n const promises: Promise<void>[] = [];\n\n if (isLazyComponent(this.config.component) && !this.component) {\n promises.push(this.loadComponent());\n }\n\n if (this.config.loader) {\n promises.push(this.loadData(route));\n }\n\n if (!promises.length) {\n this.setState(\"ready\");\n return;\n }\n\n // wait to transition to loading to avoid\n // screen flashes when the loader function\n // executes quickly\n const preloadingTimer = setTimeout(() => {\n if (this.state === \"preloading\") {\n this.setState(\"loading\");\n }\n }, 300);\n\n this.promise = Promise.all(promises)\n .then(() => {\n clearTimeout(preloadingTimer);\n if (this.state === \"loading\") {\n // if we had to transition to regular loading because\n // the loader was taking too long, force an additional\n // timeout to prevent a loading flash\n setTimeout(() => {\n this.setState(\"ready\");\n }, 300);\n } else {\n this.setState(\"ready\");\n }\n })\n .catch(() => {\n clearTimeout(preloadingTimer);\n // TODO: check for type of error\n // handle access denied and redirects\n this.setState(\"error\");\n });\n\n await this.promise;\n }\n\n setData(data: unknown) {\n this.data = data;\n }\n\n setState(state: RouteSegmentState) {\n this.state = state;\n }\n\n private async loadData(route: Route): Promise<void> {\n await this.config.loader?.(route).then((data) => this.setData(data));\n }\n\n private async loadComponent(): Promise<void> {\n if (!isLazyComponent(this.config.component)) return;\n const module = await this.config.component();\n for (const exportName in module) {\n if (exportName === \"default\" || exportName.endsWith(\"Page\")) {\n this.component = module[exportName];\n return;\n }\n }\n throw new Error(\n \"Lazy route component module did not export `default` or a `*Page` named export\",\n );\n }\n}\n","import type { NavigateOptions, RoutePath } from \"./types\";\n\nexport class Redirect<P extends RoutePath> {\n constructor(readonly options: NavigateOptions<P>) {}\n}\n\nexport const redirect = <P extends RoutePath>(options: NavigateOptions<P>): Redirect<P> =>\n new Redirect(options);\n","import { computed, makeObservable } from \"mobx\";\nimport type { Outlet } from \"./outlet\";\nimport type { Component, Guard, Obj } from \"./types\";\n\nexport interface RouteConfig {\n path: string;\n outlets: Outlet[];\n guards: Guard[];\n context?: Obj;\n layout?: Component;\n params: Obj;\n}\n\nexport class Route {\n readonly path: string;\n readonly outlets: Outlet[];\n readonly guards: Guard[];\n readonly context: Obj;\n readonly params: Obj;\n readonly layout?: Component;\n\n get data(): Obj {\n return Object.assign({}, ...this.outlets.map((o) => o.data));\n }\n\n constructor(def: RouteConfig) {\n this.path = def.path;\n this.guards = def.guards;\n this.context = def.context ?? {};\n this.outlets = def.outlets;\n this.params = def.params;\n this.layout = def.layout;\n\n makeObservable(this, {\n data: computed,\n });\n }\n\n async guard(): Promise<void> {\n for (const guard of this.guards) {\n await guard(this);\n }\n }\n\n async load(): Promise<void> {\n await Promise.all(this.outlets.map((outlet) => outlet.load(this)));\n }\n}\n","import { Outlet } from \"./outlet\";\nimport { Redirect } from \"./redirect\";\nimport { Route } from \"./route\";\nimport { CONTEXT, GUARD, LAYOUT, LOAD, PAGE, REDIRECT, WRAPPER } from \"./symbols\";\nimport type { Component, Guard, Obj, Routes } from \"./types\";\nimport { isComponent, isLazyComponent, isLeaf, isPage, isRedirect } from \"./util\";\n\nconst pathToSegments = (path: string): string[] => {\n return path.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n};\n\nexport interface MatchState {\n segments: string[];\n context: Obj;\n params: Obj;\n outlets: (Outlet | undefined)[];\n guards: (Guard | undefined)[];\n layout?: Component;\n}\n\nexport const makeRoute = (matchState: MatchState): Route => {\n const outlets = matchState.outlets.filter((o) => o !== undefined);\n const guards = matchState.guards.filter((g) => g !== undefined);\n\n return new Route({ ...matchState, outlets, guards, path: matchState.segments.join(\"/\") });\n};\n\nexport const matchRoute = (path: string, routeDef: Routes, matchState?: MatchState): Route => {\n const state: MatchState = {\n segments: [],\n params: {},\n ...matchState,\n layout: routeDef[LAYOUT] ?? matchState?.layout,\n context: { ...matchState?.context, ...routeDef[CONTEXT] },\n guards: [...(matchState?.guards ?? []), routeDef[GUARD]],\n outlets: [\n ...(matchState?.outlets ?? []),\n routeDef[WRAPPER] ? new Outlet({ component: routeDef[WRAPPER] }) : undefined,\n routeDef[LOAD] ? new Outlet({ loader: routeDef[LOAD] }) : undefined,\n ],\n };\n\n const [segment, ...remainingSegments] = pathToSegments(path);\n const remainingPath = remainingSegments.join(\"/\");\n\n let defAtSegment = routeDef[segment || \"index\"];\n\n if (!defAtSegment) {\n const matchedSegment = Object.keys(routeDef).find((segment) => segment.startsWith(\"$\"));\n if (matchedSegment) {\n defAtSegment = routeDef[matchedSegment];\n state.params[matchedSegment.slice(1)] = segment;\n }\n }\n\n if (!defAtSegment) {\n throw new Error(\"Not Found.\");\n }\n\n state.segments.push(segment ?? \"index\");\n\n if (isLeaf(defAtSegment)) {\n if (remainingPath) {\n throw new Error(\"Not Found.\");\n }\n\n if (isRedirect(defAtSegment)) {\n const redirect =\n typeof defAtSegment[REDIRECT] === \"string\"\n ? { to: defAtSegment[REDIRECT] }\n : defAtSegment[REDIRECT];\n throw new Redirect(redirect as any);\n }\n\n if (isComponent(defAtSegment) || isLazyComponent(defAtSegment)) {\n state.outlets.push(new Outlet({ component: defAtSegment }));\n return makeRoute(state);\n }\n }\n\n // at this point we have a nested route or a [Page] definition\n\n if (isPage(defAtSegment)) {\n state.layout = defAtSegment[LAYOUT] ?? state.layout;\n Object.assign(state.context, defAtSegment[CONTEXT]);\n state.guards.push(defAtSegment[GUARD]);\n state.outlets.push(\n new Outlet({\n component: defAtSegment[PAGE],\n loader: defAtSegment[LOAD],\n }),\n );\n\n return makeRoute(state);\n }\n\n // now we know we have a nested route\n return matchRoute(remainingPath, defAtSegment, state);\n};\n\n// TODO: ideally this could resolve to something less than R,\n// but specific enough to infer all paths as a literal union.\n// As it stands, there are certain things we can't access reliably\n// without the compiler complaining about circular references\n// try \"as const satisfies\" approach which would allow us to\n// exchange a less specific version of MobxRoutesRoot for this\nexport const makeRoutes =\n () =>\n <R extends Routes>(routes: R): R => {\n // todo: perform some validation here\n // - no forward slashes in keys\n // - at most one variable segment per level\n // - only lowercase letters (except variables)\n // - paths/variables cannot contain $ that aren't at the beginning\n // - path variables must be unique across a path\n\n return routes;\n };\n","import { createBrowserHistory, type History, type Location } from \"history\";\nimport { action, computed, makeObservable, observable, runInAction } from \"mobx\";\nimport { matchRoute } from \"./make-routes\";\nimport { Redirect } from \"./redirect\";\nimport type { Route } from \"./route\";\nimport type { Component, MobxRouterConfig, NavigateOptions, Obj, RoutePath, Routes } from \"./types\";\nimport { resolvePath } from \"./util\";\n\nexport interface MobxRenderSegment {\n segment: string;\n component: Component;\n props?: Obj;\n}\n\nexport class RouterStore {\n readonly history: History;\n\n routesDef?: Routes;\n\n location!: Location;\n activeRoute: Route | undefined;\n\n get search(): URLSearchParams {\n return new URLSearchParams(this.location?.search);\n }\n\n get query(): Record<string, string> {\n return Object.fromEntries(this.search);\n }\n\n get pathParams(): Record<string, string> {\n const params = {} as Record<string, string>;\n\n const paramSegments = this.activeSegments.filter((segment) => segment.startsWith(\"$\"));\n const pathValues = this.location.pathname.split(\"/\").slice(1);\n\n paramSegments.forEach((segment, index) => {\n const value = pathValues[index + 1];\n if (value) {\n params[segment] = value;\n }\n });\n\n return params as Record<string, string>;\n }\n\n get activeSegments(): string[] {\n return this.activeRoute?.path.split(\"/\") ?? [];\n }\n\n constructor(config?: MobxRouterConfig) {\n makeObservable(this, {\n location: observable.ref,\n activeRoute: observable.ref,\n\n search: computed,\n pathParams: computed,\n activeSegments: computed,\n\n setLocation: action,\n });\n\n this.history = config?.history ?? createBrowserHistory();\n }\n\n initialize(routesDef: Routes): void {\n this.routesDef = routesDef;\n this.history.listen((data) => {\n void this.setLocation(data.location);\n });\n\n void this.setLocation(this.history.location);\n }\n\n doesPathMatch<P extends RoutePath>(path: P, exact?: boolean): boolean {\n const segments = path.slice(1).split(\"/\");\n const segmentsMatch = segments.every(\n (segment, i) => segment === this.activeSegments[i] || segment.startsWith(\"$\"),\n );\n\n return (\n segmentsMatch &&\n this.activeSegments.length >= segments.length &&\n (!exact || segments.length === this.activeSegments.length)\n );\n }\n\n navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n if (!document.startViewTransition) {\n this._navigate(options);\n } else {\n const transition = document.startViewTransition(() => this._navigate(options));\n transition.ready.catch((e) => console.log(e, typeof e));\n }\n }\n\n _navigate<P extends RoutePath>(options: NavigateOptions<P>): void {\n const { to, replace, state, search = {}, preserveSearch, params } = options;\n\n const searchParams = search instanceof URLSearchParams ? search : new URLSearchParams(search);\n\n if (preserveSearch) {\n for (const [name, value] of this.search) {\n if (!searchParams.has(name)) {\n searchParams.set(name, value);\n }\n }\n }\n\n const location = {\n pathname: resolvePath(to, params),\n search: searchParams.size ? `?${searchParams.toString()}` : undefined,\n };\n\n if (replace) {\n this.history.replace(location, state);\n } else {\n this.history.push(location, state);\n }\n }\n\n setQueryParam(param: string, value: string): void {\n const params = new URLSearchParams(this.location.search);\n params.set(param, value);\n this.history.replace({ search: params.toString() });\n }\n\n removeQueryParam(param: string): string | undefined {\n const params = new URLSearchParams(this.location.search);\n const value = params.get(param) ?? undefined;\n if (value !== undefined) {\n params.delete(param);\n this.history.replace({ search: params.toString() });\n }\n return value;\n }\n\n async setLocation(location: Location): Promise<void> {\n if (!this.routesDef) return;\n\n // TODO: this should not be the responsibility of mobx-router\n // and should really be handled server-side\n if (location.pathname !== \"/\" && location.pathname.endsWith(\"/\")) {\n this.history.replace({ ...location, pathname: location.pathname.slice(0, -1) });\n return;\n }\n\n this.location = location;\n\n try {\n const matchedRoute = matchRoute(location.pathname, this.routesDef);\n\n await matchedRoute.guard();\n\n // navigating within a guard function\n // is essentially a redirect\n if (this.location !== location) {\n return;\n }\n\n runInAction(() => {\n this.activeRoute = matchedRoute;\n });\n\n await this.activeRoute?.load();\n } catch (e) {\n if (e instanceof Redirect) {\n this.navigate(e.options);\n return;\n }\n throw e;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/util.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.mts","names":[],"sources":["../src/util/mutable.ts","../src/util/use-autorun.ts"],"mappings":";;;;cAGa,OAAA,EAAS,
|
|
1
|
+
{"version":3,"file":"util.d.mts","names":[],"sources":["../src/util/mutable.ts","../src/util/use-autorun.ts"],"mappings":";;;;cAGa,OAAA,EAAS,sBAwBrB;;;iBCxBe,UAAA,CAAW,IAAA,GAAO,CAAA,EAAG,eAAA,cAA6B,OAAA,GAAU,eAAe"}
|
package/dist/util.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.mjs","names":[],"sources":["../src/util/mutable.ts","../src/util/use-autorun.ts"],"sourcesContent":["import { observable, runInAction } from \"mobx\";\nimport type { ClassAccessorDecorator } from \"mobx/dist/types/decorator_fills\";\n\nexport const mutable: ClassAccessorDecorator = (accessor) => {\n const boxedValues = new WeakMap();\n\n return {\n get() {\n let boxedValue = boxedValues.get(this);\n if (!boxedValue) {\n const value = accessor.get.call(this);\n boxedValue = observable.box(value);\n boxedValues.set(this, boxedValue);\n }\n\n return boxedValue.get();\n },\n set(value) {\n let boxedValue = boxedValues.get(this);\n if (!boxedValue) {\n boxedValue = observable.box(value);\n boxedValues.set(this, boxedValue);\n }\n\n runInAction(() => boxedValue.set(value));\n },\n };\n};\n","import { autorun, type IAutorunOptions, type IReactionPublic } from \"mobx\";\nimport { useEffect } from \"react\";\n\nexport function useAutorun(func: (r: IReactionPublic) => unknown, options?: IAutorunOptions): void {\n // biome-ignore lint/correctness/useExhaustiveDependencies: by design\n useEffect(() => autorun(func, options), []);\n}\n"],"mappings":";;;;AAGA,MAAa,WAAmC,aAAa;CAC3D,MAAM,8BAAc,IAAI,
|
|
1
|
+
{"version":3,"file":"util.mjs","names":[],"sources":["../src/util/mutable.ts","../src/util/use-autorun.ts"],"sourcesContent":["import { observable, runInAction } from \"mobx\";\nimport type { ClassAccessorDecorator } from \"mobx/dist/types/decorator_fills\";\n\nexport const mutable: ClassAccessorDecorator = (accessor) => {\n const boxedValues = new WeakMap();\n\n return {\n get() {\n let boxedValue = boxedValues.get(this);\n if (!boxedValue) {\n const value = accessor.get.call(this);\n boxedValue = observable.box(value);\n boxedValues.set(this, boxedValue);\n }\n\n return boxedValue.get();\n },\n set(value) {\n let boxedValue = boxedValues.get(this);\n if (!boxedValue) {\n boxedValue = observable.box(value);\n boxedValues.set(this, boxedValue);\n }\n\n runInAction(() => boxedValue.set(value));\n },\n };\n};\n","import { autorun, type IAutorunOptions, type IReactionPublic } from \"mobx\";\nimport { useEffect } from \"react\";\n\nexport function useAutorun(func: (r: IReactionPublic) => unknown, options?: IAutorunOptions): void {\n // biome-ignore lint/correctness/useExhaustiveDependencies: by design\n useEffect(() => autorun(func, options), []);\n}\n"],"mappings":";;;;AAGA,MAAa,WAAmC,aAAa;CAC3D,MAAM,8BAAc,IAAI,QAAQ;CAEhC,OAAO;EACL,MAAM;GACJ,IAAI,aAAa,YAAY,IAAI,IAAI;GACrC,IAAI,CAAC,YAAY;IACf,MAAM,QAAQ,SAAS,IAAI,KAAK,IAAI;IACpC,aAAa,WAAW,IAAI,KAAK;IACjC,YAAY,IAAI,MAAM,UAAU;GAClC;GAEA,OAAO,WAAW,IAAI;EACxB;EACA,IAAI,OAAO;GACT,IAAI,aAAa,YAAY,IAAI,IAAI;GACrC,IAAI,CAAC,YAAY;IACf,aAAa,WAAW,IAAI,KAAK;IACjC,YAAY,IAAI,MAAM,UAAU;GAClC;GAEA,kBAAkB,WAAW,IAAI,KAAK,CAAC;EACzC;CACF;AACF;;;;ACxBA,SAAgB,WAAW,MAAuC,SAAiC;CAEjG,gBAAgB,QAAQ,MAAM,OAAO,GAAG,CAAC,CAAC;AAC5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jayalfredprufrock/mobx-toolbox",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Misc mobx/react modules/utilities.",
|
|
5
5
|
"homepage": "https://github.com/jayalfredprufrock/mobx-toolbox#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"semantic-release": "^25.0.3",
|
|
41
41
|
"typebox": "^1.1.17",
|
|
42
42
|
"typescript": "^6.0.2",
|
|
43
|
-
"vite-plus": "
|
|
43
|
+
"vite-plus": "0.2.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"history": "^5.3.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-observable-DA4DIMbc.d.mts","names":[],"sources":["../src/lazy-observable/lazy-observable.ts"],"mappings":";;;UAUiB,cAAA,0BAAwC,CAAA;EACvD,KAAA,EAAO,aAAA;EACP,KAAA,IAAS,aAAA;EACT,SAAA,IAAa,OAAA,CAAQ,CAAA;EACrB,GAAA,CAAI,KAAA,EAAO,CAAA;EACX,MAAA,IAAU,OAAA,CAAQ,CAAA;EAClB,MAAA;EACA,KAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAGe,qBAAA;EACf,OAAA;EACA,iBAAA;EACA,SAAA;AAAA;AAAA,UAGe,qCAAA,wBAEP,qBAAA;EACR,YAAA,GAAe,aAAA;AAAA;AAAA,iBAGD,cAAA,GAAA,CAAkB,KAAA,QAAa,OAAA,CAAQ,CAAA,IAAK,cAAA,CAAe,CAAA;AAAA,iBAC3D,cAAA,GAAA,CACd,KAAA,QAAa,OAAA,CAAQ,CAAA,GACrB,OAAA,EAAS,qBAAA,GACR,cAAA,CAAe,CAAA;AAAA,iBACF,cAAA,kBAAA,CACd,KAAA,QAAa,OAAA,CAAQ,CAAA,GACrB,OAAA,EAAS,qCAAA,CAAsC,aAAA,IAC9C,cAAA,CAAe,CAAA,EAAG,aAAA;AAAA,UAkLJ,mBAAA,kBAAqC,IAAA,CACpD,cAAA,CAAe,gBAAA,CAAiB,CAAA;EAGhC,GAAA,CAAI,KAAA,EAAO,CAAA;AAAA;AAAA,UAGI,0BAAA,YAAsC,qBAAA;EACrD,YAAA,GAAe,CAAA;AAAA;AAAA,iBAGD,mBAAA,GAAA,CACd,KAAA,QAAa,OAAA,CAAQ,CAAA,KACrB,OAAA,GAAU,0BAAA,CAA2B,CAAA,IACpC,mBAAA,CAAoB,CAAA;AAAA,KAIX,mBAAA,MACV,CAAA,SAAU,mBAAA,YAA+B,CAAA,KAAM,CAAA,SAAU,cAAA,YAA0B,CAAA"}
|