modelence 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/dist/chunk-O2JTQHZP.js +2 -0
- package/dist/chunk-O2JTQHZP.js.map +1 -0
- package/dist/client.d.ts +82 -10
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/server.d.ts +316 -62
- package/dist/server.js +3 -3
- package/dist/server.js.map +1 -1
- package/dist/{types-DAn43Whw.d.ts → types-RXrmChkk.d.ts} +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# Modelence
|
|
2
2
|
|
|
3
|
-
Full-stack JavaScript framework for interactive applications
|
|
3
|
+
Full-stack JavaScript framework for interactive web applications
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Dev Setup
|
|
6
|
+
Run `npm install` first to install packages.
|
|
7
|
+
|
|
8
|
+
Use `npm run dev` to keep rebuilding the package on every change for linking in local dev mode.
|
|
9
|
+
|
|
10
|
+
## App setup
|
|
6
11
|
|
|
7
12
|
- Create the following folder structure in your Node project:
|
|
8
13
|
|
|
@@ -44,7 +49,7 @@ npm install --save-dev tailwindcss postcss autoprefixer
|
|
|
44
49
|
},
|
|
45
50
|
"sourceMap": true,
|
|
46
51
|
"noImplicitAny": true,
|
|
47
|
-
"module": "
|
|
52
|
+
"module": "NodeNext",
|
|
48
53
|
"esModuleInterop": true,
|
|
49
54
|
"skipLibCheck": true,
|
|
50
55
|
"target": "ES2020",
|
|
@@ -55,7 +60,7 @@ npm install --save-dev tailwindcss postcss autoprefixer
|
|
|
55
60
|
],
|
|
56
61
|
"jsx": "react-jsx",
|
|
57
62
|
"allowJs": true,
|
|
58
|
-
"moduleResolution": "
|
|
63
|
+
"moduleResolution": "bundler",
|
|
59
64
|
"strict": false,
|
|
60
65
|
"noEmit": true,
|
|
61
66
|
"incremental": true,
|
|
@@ -102,3 +107,9 @@ export default {
|
|
|
102
107
|
},
|
|
103
108
|
};
|
|
104
109
|
```
|
|
110
|
+
|
|
111
|
+
## Documentation
|
|
112
|
+
|
|
113
|
+
[Documentation](https://docs.modelence.com) | [API Reference](https://docs.modelence.com/api-reference)
|
|
114
|
+
|
|
115
|
+
(For open-source contributors: To generate docs when developing locally, run `npm run docs`.)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function s(n){if(n instanceof Date)return {type:"date"};if(Array.isArray(n)){let e={};for(let r=0;r<n.length;r++){let t=n[r],o=s(t);o&&(e[r]=o);}return Object.keys(e).length>0?{type:"array",elements:e}:null}if(typeof n=="object"&&n!==null){let e={};for(let[r,t]of Object.entries(n)){let o=s(t);o&&(e[r]=o);}return Object.keys(e).length>0?{type:"object",props:e}:null}return null}function i(n,e){return e?e.type==="date"?new Date(n):e.type==="array"?n.map((r,t)=>i(r,e.elements[t])):e.type==="object"?Object.fromEntries(Object.entries(n).map(([r,t])=>[r,i(t,e.props[r])])):n:n}export{s as a,i as b};//# sourceMappingURL=chunk-O2JTQHZP.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-O2JTQHZP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../methods/serialize.ts"],"names":["getResponseTypeMap","result","elements","i","item","subTypeMap","props","key","value","reviveResponseTypes","data","typeMap","index"],"mappings":"AAAO,SAASA,EAAmBC,CAAa,CAAA,CAC9C,GAAIA,CAAAA,YAAkB,KACpB,OAAO,CAAE,IAAM,CAAA,MAAO,EAGxB,GAAI,KAAA,CAAM,QAAQA,CAAM,CAAA,CAAG,CACzB,IAAMC,CAAAA,CAAgC,EAAC,CACvC,QAASC,CAAI,CAAA,CAAA,CAAGA,CAAIF,CAAAA,CAAAA,CAAO,OAAQE,CAAK,EAAA,CAAA,CACtC,IAAMC,CAAAA,CAAOH,EAAOE,CAAC,CAAA,CACfE,EAAaL,CAAmBI,CAAAA,CAAI,EACtCC,CACFH,GAAAA,CAAAA,CAASC,CAAC,CAAA,CAAIE,GAElB,CACA,OAAO,MAAO,CAAA,IAAA,CAAKH,CAAQ,CAAE,CAAA,MAAA,CAAS,CAAI,CAAA,CACxC,KAAM,OACN,CAAA,QAAA,CAAAA,CACF,CAAI,CAAA,IACN,CAEA,GAAI,OAAOD,CAAW,EAAA,QAAA,EAAYA,IAAW,IAAM,CAAA,CACjD,IAAMK,CAA6B,CAAA,GACnC,IAAW,GAAA,CAACC,CAAKC,CAAAA,CAAK,IAAK,MAAO,CAAA,OAAA,CAAQP,CAAM,CAAG,CAAA,CACjD,IAAMI,CAAaL,CAAAA,CAAAA,CAAmBQ,CAAK,CAAA,CACvCH,IACFC,CAAMC,CAAAA,CAAG,CAAIF,CAAAA,CAAAA,EAEjB,CACA,OAAO,MAAA,CAAO,IAAKC,CAAAA,CAAK,EAAE,MAAS,CAAA,CAAA,CAAI,CACrC,IAAM,CAAA,QAAA,CACN,MAAAA,CACF,CAAA,CAAI,IACN,CAEA,OAAO,IACT,CAEO,SAASG,CAA6BC,CAAAA,CAAAA,CAAWC,EAAkC,CACxF,OAAKA,CAIDA,CAAAA,CAAAA,CAAQ,OAAS,MACZ,CAAA,IAAI,KAAKD,CAAI,CAAA,CAGlBC,EAAQ,IAAS,GAAA,OAAA,CACZD,CAAK,CAAA,GAAA,CAAI,CAACN,CAAWQ,CAAAA,CAAAA,GAAkBH,CAAoBL,CAAAA,CAAAA,CAAMO,EAAQ,QAASC,CAAAA,CAAK,CAAC,CAAC,EAG9FD,CAAQ,CAAA,IAAA,GAAS,SACZ,MAAO,CAAA,WAAA,CAAY,OAAO,OAAQD,CAAAA,CAAI,CAAE,CAAA,GAAA,CAAI,CAAC,CAACH,CAAAA,CAAKC,CAAK,CAAM,GAAA,CAACD,EAAKE,CAAoBD,CAAAA,CAAAA,CAAOG,CAAQ,CAAA,KAAA,CAAMJ,CAAG,CAAC,CAAC,CAAC,CAAC,CAAA,CAGtHG,EAfEA,CAgBX","file":"chunk-O2JTQHZP.js","sourcesContent":["export function getResponseTypeMap(result: any) {\n if (result instanceof Date) {\n return { type: 'date' };\n }\n\n if (Array.isArray(result)) {\n const elements: Record<string, any> = {};\n for (let i = 0; i < result.length; i++) {\n const item = result[i];\n const subTypeMap = getResponseTypeMap(item);\n if (subTypeMap) {\n elements[i] = subTypeMap;\n }\n }\n return Object.keys(elements).length > 0 ? {\n type: 'array',\n elements\n } : null;\n }\n\n if (typeof result === 'object' && result !== null) {\n const props: Record<string, any> = {};\n for (const [key, value] of Object.entries(result)) {\n const subTypeMap = getResponseTypeMap(value);\n if (subTypeMap) {\n props[key] = subTypeMap;\n }\n }\n return Object.keys(props).length > 0 ? {\n type: 'object',\n props\n } : null;\n }\n\n return null;\n}\n\nexport function reviveResponseTypes<T = any>(data: any, typeMap?: Record<string, any>): T {\n if (!typeMap) {\n return data;\n }\n\n if (typeMap.type === 'date') {\n return new Date(data) as T;\n }\n\n if (typeMap.type === 'array') {\n return data.map((item: any, index: number) => reviveResponseTypes(item, typeMap.elements[index]));\n }\n\n if (typeMap.type === 'object') {\n return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, reviveResponseTypes(value, typeMap.props[key])])) as T;\n }\n\n return data;\n}\n"]}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as ConfigKey } from './types-RXrmChkk.js';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
declare function getConfig(key: ConfigKey): string | number | boolean;
|
|
@@ -14,29 +14,101 @@ declare function renderApp({ loadingElement, routesElement, favicon, errorHandle
|
|
|
14
14
|
|
|
15
15
|
type Args = Record<string, unknown>;
|
|
16
16
|
type MethodResult<T> = {
|
|
17
|
-
|
|
17
|
+
isFetching: boolean;
|
|
18
18
|
error: Error | null;
|
|
19
19
|
data: T | null;
|
|
20
20
|
};
|
|
21
21
|
declare function callMethod<T = unknown>(methodName: string, args?: Args): Promise<T>;
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* React hook for executing a query method.
|
|
24
|
+
*
|
|
25
|
+
* This hook automatically executes the query on mount and provides a refetch capability.
|
|
26
|
+
* Similar to React Query's useQuery hook.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam T - The expected return type of the query
|
|
29
|
+
* @param methodName - The name of the method to query
|
|
30
|
+
* @param args - Optional arguments to pass to the method
|
|
31
|
+
* @returns {Object} An object containing the query state and a refetch function:
|
|
32
|
+
* - `data` - The data returned by the query, or null if not yet loaded
|
|
33
|
+
* - `isFetching` - Boolean indicating if the query is in progress
|
|
34
|
+
* - `error` - Any error that occurred during the query, or null
|
|
35
|
+
* - `refetch` - Function to manually trigger a refetch with optional new arguments
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* function MyComponent() {
|
|
40
|
+
* // This is assuming you have a Module named "todo" with a query named "getItem"
|
|
41
|
+
* const { data, isFetching, error } = useQuery<Todo>('todo.getItem', { id: '123' });
|
|
42
|
+
* if (isFetching) {
|
|
43
|
+
* return <div>Loading...</div>;
|
|
44
|
+
* }
|
|
45
|
+
* if (error) {
|
|
46
|
+
* return <div>Error: {error.message}</div>;
|
|
47
|
+
* }
|
|
48
|
+
* return <div>{data?.name}</div>;
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function useQuery<T = unknown>(methodName: string, args?: Args): MethodResult<T> & {
|
|
53
|
+
/** Function to manually trigger a refetch of the query with optional new arguments */
|
|
54
|
+
refetch: (args?: Args) => void;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* React hook for executing a mutation method.
|
|
58
|
+
*
|
|
59
|
+
* This hook provides functions to trigger the mutation manually and handles loading/error states.
|
|
60
|
+
* Similar to React Query's useMutation hook.
|
|
61
|
+
*
|
|
62
|
+
* @typeParam T - The expected return type of the mutation
|
|
63
|
+
* @param methodName - The name of the method to mutate
|
|
64
|
+
* @param args - Optional default arguments to pass to the method
|
|
65
|
+
* @returns {Object} An object containing the mutation state and trigger functions:
|
|
66
|
+
* - `data` - The data returned by the last successful mutation, or null
|
|
67
|
+
* - `isFetching` - Boolean indicating if the mutation is in progress
|
|
68
|
+
* - `error` - Any error that occurred during the last mutation, or null
|
|
69
|
+
* - `mutate` - Function to trigger the mutation with optional arguments
|
|
70
|
+
* - `mutateAsync` - Promise-returning version of mutate, useful for awaiting the result
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* const { mutate: updateTodo, isFetching, error } = useMutation<User>('todos.update');
|
|
75
|
+
*
|
|
76
|
+
* // Later in your code:
|
|
77
|
+
* updateTodo({ id: '123', name: 'New Name' });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
declare function useMutation<T = unknown>(methodName: string, args?: Args): MethodResult<T> & {
|
|
81
|
+
/** Function to trigger the mutation with optional arguments */
|
|
82
|
+
mutate: (args?: Args) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Async version of mutate that returns a promise with the result.
|
|
85
|
+
* Useful when you need to wait for the mutation to complete.
|
|
86
|
+
*/
|
|
87
|
+
mutateAsync: (args?: Args) => Promise<T>;
|
|
88
|
+
};
|
|
23
89
|
|
|
90
|
+
type User$1 = {
|
|
91
|
+
id: string;
|
|
92
|
+
handle: string;
|
|
93
|
+
};
|
|
24
94
|
declare function useSession(): {
|
|
25
|
-
user:
|
|
26
|
-
id: string;
|
|
27
|
-
handle: string;
|
|
28
|
-
} | null;
|
|
95
|
+
user: User$1 | null;
|
|
29
96
|
};
|
|
30
97
|
|
|
98
|
+
type User = {
|
|
99
|
+
id: string;
|
|
100
|
+
handle: string;
|
|
101
|
+
};
|
|
31
102
|
declare function signupWithPassword({ email, password }: {
|
|
32
103
|
email: string;
|
|
33
104
|
password: string;
|
|
34
|
-
}): Promise<
|
|
105
|
+
}): Promise<void>;
|
|
35
106
|
declare function loginWithPassword({ email, password }: {
|
|
36
107
|
email: string;
|
|
37
108
|
password: string;
|
|
38
|
-
}): Promise<
|
|
109
|
+
}): Promise<User>;
|
|
110
|
+
declare function logout(): Promise<void>;
|
|
39
111
|
|
|
40
112
|
declare const AppProvider: any;
|
|
41
113
|
|
|
42
|
-
export { AppProvider, callMethod, getConfig, loginWithPassword, renderApp, signupWithPassword,
|
|
114
|
+
export { AppProvider, callMethod, getConfig, loginWithPassword, logout, renderApp, signupWithPassword, useMutation, useQuery, useSession };
|
package/dist/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {a}from'./chunk-GDI7FXAT.js';import
|
|
1
|
+
import {b as b$1}from'./chunk-O2JTQHZP.js';import {a}from'./chunk-GDI7FXAT.js';import U,{useState,useEffect,useMemo}from'react';import {create}from'zustand';import {z as z$1}from'zod';import {jsx,Fragment}from'react/jsx-runtime';import q from'react-dom/client';function h(){let e=localStorage.getItem("modelence.session");try{return e?JSON.parse(e):null}catch(r){return console.error("Error parsing session from localStorage",r),null}}function w(e){localStorage.setItem("modelence.session",JSON.stringify(e));}var S=(e,r)=>{throw new Error(`Error calling method '${r}': ${e.toString()}`)};function y(e){S=e;}function E(e,r){return S(e,r)}async function i(e,r={}){try{return await O(`/api/_internal/method/${e}`,r)}catch(t){throw E(t,e),t}}async function O(e,r){let t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({args:r,authToken:h()?.authToken,clientInfo:{screenWidth:window.screen.width,screenHeight:window.screen.height,windowWidth:window.innerWidth,windowHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,orientation:window.screen.orientation?.type}})});if(!t.ok){let s=await t.text();throw new Error(s)}let o=await t.text(),n=o?JSON.parse(o):undefined;if(!n)throw new Error("Invalid response from server");return b$1(n.data,n.typeMap)}function _(e,r={}){let{result:t,triggerMethod:o}=A(e,r,{enabled:true});return {...t,refetch:n=>o(n)}}function W(e,r={}){let{result:t,triggerMethod:o}=A(e,r,{enabled:false});return {...t,mutate:n=>o(n),mutateAsync:o}}function A(e,r={},t){let o=useMemo(()=>r,[JSON.stringify(r)]),[n,s]=useState({isFetching:t.enabled,error:null,data:null}),p=async(k=o)=>{s({isFetching:true,error:null,data:n.data});try{let a=await i(e,k);return s({isFetching:!1,error:null,data:a}),a}catch(a){throw s({isFetching:false,error:a,data:null}),a}};return useEffect(()=>{t.enabled&&p();},[e,o]),{result:n,triggerMethod:p}}var l={};function J(e){if(!(e in l))throw new Error(`Unknown config: ${e}`);return l[e]?.value}function x(e){l=e;}var u=create(e=>({user:null,setUser:r=>e({user:r})})),T=false,j=a.seconds(30);async function R(){if(T)return;T=true;let{configs:e,session:r,user:t}=await i("_system.session.init");x(e),w(r);let o=t?Object.freeze(z$1.object({id:z$1.string(),handle:z$1.string()}).parse(t)):null;u.getState().setUser(o),await M();}async function M(){await i("_system.session.heartbeat"),setTimeout(M,j);}function d(e){u.getState().setUser(e);}function z(){return {user:u(r=>r.user)}}var P=false;function g({children:e,loadingElement:r}){let[t,o]=useState(true);return useEffect(()=>{async function n(){P||(P=true,await R(),o(false));}n();},[]),t?r??jsx("div",{children:"Loading..."}):jsx(Fragment,{children:e})}function D({loadingElement:e,routesElement:r,favicon:t,errorHandler:o}){if(o&&y(o),window.addEventListener("unload",()=>{}),q.createRoot(document.getElementById("root")).render(jsx(U.StrictMode,{children:jsx(b,{loadingElement:e,children:r})})),t){let n=document.querySelector("link[rel~='icon']");if(n)n.href=t;else {let s=document.createElement("link");s.rel="icon",s.href=t,document.head.appendChild(s);}}}async function V({email:e,password:r}){await i("_system.user.signupWithPassword",{email:e,password:r}),await H({email:e,password:r});}async function H({email:e,password:r}){let{user:t}=await i("_system.user.loginWithPassword",{email:e,password:r});return d(t),t}async function G(){await i("_system.user.logout"),d(null);}var b="useClient"in U?U.useClient(g):g;export{b as AppProvider,i as callMethod,J as getConfig,H as loginWithPassword,G as logout,D as renderApp,V as signupWithPassword,W as useMutation,_ as useQuery,z as useSession};//# sourceMappingURL=client.js.map
|
|
2
2
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../client/localStorage.ts","../client/errorHandler.ts","../client/method.ts","../config/client.ts","../client/session.ts","../client/AppProvider.tsx","../client/renderApp.tsx","../auth/client/index.ts","../client.ts"],"names":["getLocalStorageSession","sessionJson","e","setLocalStorageSession","session","errorHandler","error","methodName","setErrorHandler","handler","handleError","callMethod","args","call","endpoint","response","text","useLoader","stableArgs","useMemo","result","setResult","useState","useEffect","data","config","getConfig","key","_setConfig","configs","isInitialized","SESSION_HEARTBEAT_INTERVAL","time","currentUser","initSession","user","z","loopSessionHeartbeat","useSession","setUser","AppProvider","children","loadingElement","isLoading","setIsLoading","initConfig","jsx","Fragment","renderApp","routesElement","favicon","ReactDOM","React","link","newLink","signupWithPassword","email","password","loginWithPassword"],"mappings":"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","file":"client.js","sourcesContent":["export function getLocalStorageSession() {\n const sessionJson = localStorage.getItem('modelence.session');\n try {\n return sessionJson ? JSON.parse(sessionJson) : null;\n } catch (e) {\n console.error('Error parsing session from localStorage', e);\n return null;\n }\n}\n\nexport function setLocalStorageSession(session: object) {\n localStorage.setItem('modelence.session', JSON.stringify(session));\n}\n","export type ErrorHandler = (error: Error, methodName: string) => void;\n\nlet errorHandler: ErrorHandler = (error, methodName) => {\n throw new Error(`Error calling method '${methodName}': ${error.toString()}`);\n};\n\nexport function setErrorHandler(handler: ErrorHandler) {\n errorHandler = handler;\n}\n\nexport function handleError(error: Error, methodName: string) {\n return errorHandler(error, methodName);\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport { useState, useEffect, useMemo } from 'react';\nimport { getLocalStorageSession } from './localStorage';\nimport { handleError } from './errorHandler';\n\ntype Args = Record<string, unknown>;\n\ntype MethodResult<T> = {\n isLoading: boolean;\n error: Error | null;\n data: T | null;\n};\n\nexport async function callMethod<T = unknown>(methodName: string, args: Args = {}): Promise<T> {\n try {\n return await call<T>(`/api/_internal/method/${methodName}`, args);\n } catch (error) {\n handleError(error as Error, methodName);\n throw error;\n }\n}\n\nasync function call<T = unknown>(endpoint: string, args: Args): Promise<T> {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n args,\n authToken: getLocalStorageSession()?.authToken,\n clientInfo: {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n pixelRatio: window.devicePixelRatio,\n orientation: window.screen.orientation?.type\n }\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(error);\n }\n\n const text = await response.text();\n return text ? JSON.parse(text) : undefined;\n}\n\nexport function useLoader<T>(methodName: string, args: Args = {}): MethodResult<T> {\n // Memoize the args object to maintain reference stability and prevent infinite re-renders\n const stableArgs = useMemo(() => args, [JSON.stringify(args)]);\n\n const [result, setResult] = useState<MethodResult<T>>({\n isLoading: true,\n error: null,\n data: null,\n });\n\n // TODO: switch to React Query (TanStack Query)\n useEffect(() => {\n const fetchData = async () => {\n try {\n const data = await callMethod<T>(methodName, stableArgs);\n setResult({ isLoading: false, error: null, data });\n } catch (error) {\n setResult({ isLoading: false, error: error as Error, data: null });\n }\n };\n\n fetchData();\n }, [methodName, stableArgs]);\n\n return result;\n}\n","import { ConfigKey, AppConfig } from './types';\n\nlet config: Record<ConfigKey, AppConfig> = {};\n\nexport function getConfig(key: ConfigKey) {\n return config[key]?.value;\n}\n\nexport function _setConfig(configs: Record<ConfigKey, AppConfig>) {\n config = configs;\n}\n","'use client';\n\nimport { z } from 'zod';\nimport { callMethod } from './method';\nimport { ConfigKey, AppConfig } from '../config/types';\nimport { _setConfig } from '../config/client';\nimport { useState, useEffect } from 'react';\nimport { setLocalStorageSession } from './localStorage';\nimport { time } from '../time';\n\ntype Configs = Record<ConfigKey, AppConfig>;\n\nlet isInitialized = false;\nconst SESSION_HEARTBEAT_INTERVAL = time.seconds(30);\n\nlet currentUser: {\n id: string;\n handle: string;\n} | null = null;\n\nexport async function initSession() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n const { configs, session, user } = await callMethod<{ configs: Configs, session: object, user: object }>('_system.session.init');\n _setConfig(configs);\n setLocalStorageSession(session);\n currentUser = user ? Object.freeze(z.object({\n id: z.string(),\n handle: z.string(),\n }).parse(user)) : null;\n\n await loopSessionHeartbeat();\n}\n\nasync function loopSessionHeartbeat() {\n await callMethod('_system.session.heartbeat');\n setTimeout(loopSessionHeartbeat, SESSION_HEARTBEAT_INTERVAL);\n}\n\nexport function useSession() {\n const [user, setUser] = useState(currentUser);\n\n // TODO: re-fetch the user on demand\n useEffect(() => {\n // Fetch and update currentUser\n }, []);\n\n return { user };\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport React, { useState, useEffect, ReactNode } from 'react';\nimport { initSession } from './session';\n\ninterface AppProviderProps {\n children: ReactNode;\n loadingElement?: ReactNode;\n}\n\nexport function AppProvider({ children, loadingElement }: AppProviderProps) {\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n async function initConfig() {\n await initSession();\n setIsLoading(false);\n }\n\n initConfig();\n }, []);\n\n if (isLoading) {\n return loadingElement ?? <div>Loading...</div>;\n }\n\n return <>{children}</>;\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { AppProvider } from '../client';\nimport { setErrorHandler, ErrorHandler } from './errorHandler';\n\nexport function renderApp({ loadingElement, routesElement, favicon, errorHandler }: {\n loadingElement: React.ReactNode,\n routesElement: React.ReactNode,\n favicon?: string,\n errorHandler?: ErrorHandler\n}) {\n if (errorHandler) {\n setErrorHandler(errorHandler);\n }\n\n window.addEventListener('unload', () => {\n // The presence of any 'unload' event handler, even empty,\n // prevents bfcache in most browsers\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <AppProvider loadingElement={loadingElement}>\n {routesElement}\n </AppProvider>\n </React.StrictMode>\n );\n\n if (favicon) {\n const link = document.querySelector(\"link[rel~='icon']\") as HTMLLinkElement;\n if (!link) {\n const newLink = document.createElement('link');\n newLink.rel = 'icon';\n newLink.href = favicon;\n document.head.appendChild(newLink);\n } else {\n link.href = favicon;\n }\n }\n}\n","import { callMethod } from '../../client/method';\n\nexport async function signupWithPassword({ email, password }: { email: string, password: string }) {\n return callMethod('_system.user.signupWithPassword', { email, password });\n}\n\nexport async function loginWithPassword({ email, password }: { email: string, password: string }) {\n return callMethod('_system.user.loginWithPassword', { email, password });\n}\n","import React from 'react';\n\nimport { AppProvider as OriginalAppProvider } from './client/AppProvider';\n\nexport { getConfig } from './config/client';\n\nexport const AppProvider = 'useClient' in React\n // @ts-ignore: React.useClient only exists in Next.js\n ? React.useClient(OriginalAppProvider)\n : OriginalAppProvider;\n\nexport { renderApp } from './client/renderApp';\nexport { useLoader, callMethod } from './client/method';\nexport { useSession } from './client/session';\nexport { signupWithPassword, loginWithPassword } from './auth/client';\n"]}
|
|
1
|
+
{"version":3,"sources":["../client/localStorage.ts","../client/errorHandler.ts","../client/method.ts","../config/client.ts","../client/session.ts","../client/AppProvider.tsx","../client/renderApp.tsx","../auth/client/index.ts","../client.ts"],"names":["getLocalStorageSession","sessionJson","e","setLocalStorageSession","session","errorHandler","error","methodName","setErrorHandler","handler","handleError","callMethod","args","call","endpoint","response","text","result","reviveResponseTypes","useQuery","triggerMethod","useMethod","useMutation","options","stableArgs","useMemo","setResult","useState","data","useEffect","config","getConfig","key","_setConfig","configs","useSessionStore","create","set","user","isInitialized","SESSION_HEARTBEAT_INTERVAL","time","initSession","parsedUser","z","loopSessionHeartbeat","setCurrentUser","useSession","state","AppProvider","children","loadingElement","isLoading","setIsLoading","initConfig","jsx","Fragment","renderApp","routesElement","favicon","ReactDOM","React","link","newLink","signupWithPassword","email","password","loginWithPassword","logout"],"mappings":"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","file":"client.js","sourcesContent":["export function getLocalStorageSession() {\n const sessionJson = localStorage.getItem('modelence.session');\n try {\n return sessionJson ? JSON.parse(sessionJson) : null;\n } catch (e) {\n console.error('Error parsing session from localStorage', e);\n return null;\n }\n}\n\nexport function setLocalStorageSession(session: object) {\n localStorage.setItem('modelence.session', JSON.stringify(session));\n}\n","export type ErrorHandler = (error: Error, methodName: string) => void;\n\nlet errorHandler: ErrorHandler = (error, methodName) => {\n throw new Error(`Error calling method '${methodName}': ${error.toString()}`);\n};\n\nexport function setErrorHandler(handler: ErrorHandler) {\n errorHandler = handler;\n}\n\nexport function handleError(error: Error, methodName: string) {\n return errorHandler(error, methodName);\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport { useState, useEffect, useMemo } from 'react';\nimport { getLocalStorageSession } from './localStorage';\nimport { handleError } from './errorHandler';\nimport { reviveResponseTypes } from '../methods/serialize';\n\ntype Args = Record<string, unknown>;\n\ntype MethodResult<T> = {\n isFetching: boolean;\n error: Error | null;\n data: T | null;\n};\n\nexport async function callMethod<T = unknown>(methodName: string, args: Args = {}): Promise<T> {\n try {\n return await call<T>(`/api/_internal/method/${methodName}`, args);\n } catch (error) {\n handleError(error as Error, methodName);\n throw error;\n }\n}\n\nasync function call<T = unknown>(endpoint: string, args: Args): Promise<T> {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n args,\n authToken: getLocalStorageSession()?.authToken,\n clientInfo: {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n pixelRatio: window.devicePixelRatio,\n orientation: window.screen.orientation?.type\n }\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(error);\n }\n\n const text = await response.text();\n const result = text ? JSON.parse(text) : undefined;\n if (!result) {\n throw new Error('Invalid response from server');\n }\n\n return reviveResponseTypes(result.data, result.typeMap);\n}\n\n/**\n * React hook for executing a query method.\n * \n * This hook automatically executes the query on mount and provides a refetch capability.\n * Similar to React Query's useQuery hook.\n * \n * @typeParam T - The expected return type of the query\n * @param methodName - The name of the method to query\n * @param args - Optional arguments to pass to the method\n * @returns {Object} An object containing the query state and a refetch function:\n * - `data` - The data returned by the query, or null if not yet loaded\n * - `isFetching` - Boolean indicating if the query is in progress\n * - `error` - Any error that occurred during the query, or null\n * - `refetch` - Function to manually trigger a refetch with optional new arguments\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * // This is assuming you have a Module named \"todo\" with a query named \"getItem\"\n * const { data, isFetching, error } = useQuery<Todo>('todo.getItem', { id: '123' });\n * if (isFetching) {\n * return <div>Loading...</div>;\n * }\n * if (error) {\n * return <div>Error: {error.message}</div>;\n * }\n * return <div>{data?.name}</div>;\n * }\n * ```\n */\nexport function useQuery<T = unknown>(methodName: string, args: Args = {}): MethodResult<T> & {\n /** Function to manually trigger a refetch of the query with optional new arguments */\n refetch: (args?: Args) => void\n} {\n const { result, triggerMethod } = useMethod<T>(methodName, args, { enabled: true });\n return {\n ...result,\n refetch: (args?: Args) => triggerMethod(args),\n };\n}\n\n/**\n * React hook for executing a mutation method.\n * \n * This hook provides functions to trigger the mutation manually and handles loading/error states.\n * Similar to React Query's useMutation hook.\n * \n * @typeParam T - The expected return type of the mutation\n * @param methodName - The name of the method to mutate\n * @param args - Optional default arguments to pass to the method\n * @returns {Object} An object containing the mutation state and trigger functions:\n * - `data` - The data returned by the last successful mutation, or null\n * - `isFetching` - Boolean indicating if the mutation is in progress\n * - `error` - Any error that occurred during the last mutation, or null\n * - `mutate` - Function to trigger the mutation with optional arguments\n * - `mutateAsync` - Promise-returning version of mutate, useful for awaiting the result\n * \n * @example\n * ```tsx\n * const { mutate: updateTodo, isFetching, error } = useMutation<User>('todos.update');\n * \n * // Later in your code:\n * updateTodo({ id: '123', name: 'New Name' });\n * ```\n */\nexport function useMutation<T = unknown>(methodName: string, args: Args = {}): MethodResult<T> & {\n /** Function to trigger the mutation with optional arguments */\n mutate: (args?: Args) => void,\n /** \n * Async version of mutate that returns a promise with the result.\n * Useful when you need to wait for the mutation to complete.\n */\n mutateAsync: (args?: Args) => Promise<T>\n} {\n const { result, triggerMethod } = useMethod<T>(methodName, args, { enabled: false });\n return {\n ...result,\n mutate: (args?: Args) => triggerMethod(args),\n mutateAsync: triggerMethod,\n };\n}\n\nexport function useMethod<T>(methodName: string, args: Args = {}, options: { enabled: boolean }): {\n result: MethodResult<T>,\n triggerMethod: (args?: Args) => Promise<T>\n} {\n // Memoize the args object to maintain reference stability and prevent infinite re-renders\n const stableArgs = useMemo(() => args, [JSON.stringify(args)]);\n\n const [result, setResult] = useState<MethodResult<T>>({\n isFetching: options.enabled,\n error: null,\n data: null,\n });\n\n const triggerMethod = async (args: Args = stableArgs) => {\n setResult({ isFetching: true, error: null, data: result.data });\n try {\n const data = await callMethod<T>(methodName, args);\n setResult({ isFetching: false, error: null, data });\n return data;\n } catch (error) {\n setResult({ isFetching: false, error: error as Error, data: null });\n throw error;\n }\n };\n\n // TODO: switch to React Query (TanStack Query)\n useEffect(() => {\n if (!options.enabled) {\n return;\n }\n\n triggerMethod();\n }, [methodName, stableArgs]);\n\n return { result, triggerMethod };\n}\n","import { ConfigKey, AppConfig, Configs } from './types';\n\nlet config: Record<ConfigKey, AppConfig> = {};\n\nexport function getConfig(key: ConfigKey) {\n if (!(key in config)) {\n throw new Error(`Unknown config: ${key}`);\n }\n\n return config[key]?.value;\n}\n\nexport function _setConfig(configs: Configs) {\n config = configs;\n}\n","import { create } from 'zustand';\nimport { z } from 'zod';\nimport { callMethod } from './method';\nimport { _setConfig } from '../config/client';\nimport { setLocalStorageSession } from './localStorage';\nimport { time } from '../time';\nimport { Configs } from '../config/types';\n\ntype User = {\n id: string;\n handle: string;\n};\n\ntype SessionStore = {\n user: User | null;\n setUser: (user: User | null) => void;\n};\n\nexport const useSessionStore = create<SessionStore>((set) => ({\n user: null,\n setUser: (user) => set({ user }),\n}));\n\nlet isInitialized = false;\nconst SESSION_HEARTBEAT_INTERVAL = time.seconds(30);\n\nexport async function initSession() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n const { configs, session, user } = await callMethod<{ configs: Configs, session: object, user: object }>('_system.session.init');\n _setConfig(configs);\n setLocalStorageSession(session);\n \n const parsedUser = user ? Object.freeze(z.object({\n id: z.string(),\n handle: z.string(),\n }).parse(user)) : null;\n\n useSessionStore.getState().setUser(parsedUser);\n\n await loopSessionHeartbeat();\n}\n\nasync function loopSessionHeartbeat() {\n await callMethod('_system.session.heartbeat');\n setTimeout(loopSessionHeartbeat, SESSION_HEARTBEAT_INTERVAL);\n}\n\nexport function setCurrentUser(user: User | null) {\n useSessionStore.getState().setUser(user);\n}\n\nexport function useSession() {\n const user = useSessionStore(state => state.user);\n return { user };\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport React, { useState, useEffect, ReactNode } from 'react';\nimport { initSession } from './session';\n\ninterface AppProviderProps {\n children: ReactNode;\n loadingElement?: ReactNode;\n}\n\nlet isInitialized = false;\n\nexport function AppProvider({ children, loadingElement }: AppProviderProps) {\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n async function initConfig() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n await initSession();\n setIsLoading(false);\n }\n\n initConfig();\n }, []);\n\n if (isLoading) {\n return loadingElement ?? <div>Loading...</div>;\n }\n\n return <>{children}</>;\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { AppProvider } from '../client';\nimport { setErrorHandler, ErrorHandler } from './errorHandler';\n\nexport function renderApp({ loadingElement, routesElement, favicon, errorHandler }: {\n loadingElement: React.ReactNode,\n routesElement: React.ReactNode,\n favicon?: string,\n errorHandler?: ErrorHandler\n}) {\n if (errorHandler) {\n setErrorHandler(errorHandler);\n }\n\n window.addEventListener('unload', () => {\n // The presence of any 'unload' event handler, even empty,\n // prevents bfcache in most browsers\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <AppProvider loadingElement={loadingElement}>\n {routesElement}\n </AppProvider>\n </React.StrictMode>\n );\n\n if (favicon) {\n const link = document.querySelector(\"link[rel~='icon']\") as HTMLLinkElement;\n if (!link) {\n const newLink = document.createElement('link');\n newLink.rel = 'icon';\n newLink.href = favicon;\n document.head.appendChild(newLink);\n } else {\n link.href = favicon;\n }\n }\n}\n","import { setCurrentUser } from '../../client/session';\nimport { callMethod } from '../../client/method';\n\ntype User = {\n id: string;\n handle: string;\n};\n\nexport async function signupWithPassword({ email, password }: { email: string, password: string }) {\n await callMethod('_system.user.signupWithPassword', { email, password });\n\n // TODO: handle auto-login from the signup method itself to avoid a second method call\n await loginWithPassword({ email, password });\n}\n\nexport async function loginWithPassword({ email, password }: { email: string, password: string }) {\n const { user } = await callMethod<{ user: User }>('_system.user.loginWithPassword', { email, password });\n setCurrentUser(user);\n return user;\n}\n\nexport async function logout() {\n await callMethod('_system.user.logout');\n setCurrentUser(null);\n}","import React from 'react';\n\nimport { AppProvider as OriginalAppProvider } from './client/AppProvider';\n\nexport { getConfig } from './config/client';\n\nexport const AppProvider = 'useClient' in React\n // @ts-ignore: React.useClient only exists in Next.js\n ? React.useClient(OriginalAppProvider)\n : OriginalAppProvider;\n\nexport { renderApp } from './client/renderApp';\nexport { useQuery, useMutation, callMethod } from './client/method';\nexport { useSession } from './client/session';\nexport { signupWithPassword, loginWithPassword, logout } from './auth/client';\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
export { C as ConfigSchema } from './types-RXrmChkk.js';
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
declare const time: {
|
|
4
|
+
seconds: (x: number) => number;
|
|
5
|
+
minutes: (x: number) => number;
|
|
6
|
+
hours: (x: number) => number;
|
|
7
|
+
days: (x: number) => number;
|
|
8
|
+
weeks: (x: number) => number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { time };
|