@metagptx/web-sdk 0.0.49 → 0.0.52-beta.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.
Files changed (3) hide show
  1. package/README.md +169 -2
  2. package/dist/index.js +1 -1
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # FuncSea WebSDK
2
2
 
3
- A TypeScript SDK for interacting with FuncSea API, providing modules for authentication, entity management, API calls, and integrations.
3
+ A TypeScript SDK for interacting with FuncSea API, providing modules for authentication, entity management, API calls, integrations, and a Vite plugin for automatic 404 page setup.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,6 +14,8 @@ yarn add @metagptx/web-sdk
14
14
 
15
15
  ## Quick Start
16
16
 
17
+ ### Basic Usage
18
+
17
19
  ```typescript
18
20
  import { createClient } from '@metagptx/web-sdk';
19
21
 
@@ -24,9 +26,27 @@ const client = createClient();
24
26
  const user = await client.auth.me();
25
27
  ```
26
28
 
29
+ ### Using the Vite Plugin
30
+
31
+ If you're using Vite with React Router, you can use the included plugin to automatically add a 404 page:
32
+
33
+ ```typescript
34
+ // vite.config.ts
35
+ import { defineConfig } from 'vite';
36
+ import react from '@vitejs/plugin-react';
37
+ import { vitePlugin404 } from '@metagptx/web-sdk';
38
+
39
+ export default defineConfig({
40
+ plugins: [
41
+ react(),
42
+ vitePlugin404(), // Automatically adds 404 page to your routes
43
+ ],
44
+ });
45
+ ```
46
+
27
47
  ## Modules
28
48
 
29
- The SDK provides six main modules:
49
+ The SDK provides six main modules and a Vite plugin:
30
50
 
31
51
  - **auth**: User authentication operations
32
52
  - **entities**: Dynamic entity CRUD operations
@@ -34,6 +54,7 @@ The SDK provides six main modules:
34
54
  - **integrations**: Integration function invocations
35
55
  - **frame**: Frame communication operations for iframe/parent window messaging
36
56
  - **utils**: Utility functions for URL opening and window management
57
+ - **vitePlugin404**: Vite plugin for automatically adding a 404 page to React Router applications
37
58
 
38
59
  ## API Reference
39
60
 
@@ -598,6 +619,117 @@ The SDK automatically handles 401 (Unauthorized) responses by redirecting to the
598
619
 
599
620
  ---
600
621
 
622
+ ## Vite Plugin
623
+
624
+ The SDK provides a Vite plugin for automatically adding a 404 page to your React Router application.
625
+
626
+ ### `vitePlugin404()`
627
+
628
+ A Vite plugin that automatically creates a virtual 404 React component and adds it to your routing configuration.
629
+
630
+ **Features:**
631
+ - Automatically creates a beautiful 404 page component
632
+ - Automatically adds a wildcard route (`path="*"`) to your `App.tsx`
633
+ - Detects if already configured (won't duplicate routes)
634
+ - Works seamlessly with React Router
635
+ - Provides "Create page" functionality when running in MGX iframe
636
+
637
+ **Setup:**
638
+
639
+ 1. Import the plugin in your `vite.config.ts`:
640
+
641
+ ```typescript
642
+ import { defineConfig } from 'vite';
643
+ import react from '@vitejs/plugin-react';
644
+ import { vitePlugin404 } from '@metagptx/web-sdk';
645
+
646
+ export default defineConfig({
647
+ plugins: [
648
+ react(),
649
+ vitePlugin404(), // Add the plugin
650
+ ],
651
+ });
652
+ ```
653
+
654
+ 2. The plugin will automatically:
655
+ - Create a virtual `NotFoundPage` component
656
+ - Add the import statement to your `App.tsx`
657
+ - Add a `<Route path="*" element={<NotFoundPage />} />` route
658
+
659
+ **Requirements:**
660
+
661
+ - Your project must have an `App.tsx` file (or any file containing `App.tsx` in the path)
662
+ - Your project must use React Router with `<Routes>` component
663
+ - Your project should have `@/components/ui/button` component (or adjust the import in the plugin code)
664
+
665
+ **How it works:**
666
+
667
+ The plugin uses multiple strategies to intelligently add the 404 route:
668
+
669
+ 1. **Import placement strategies:**
670
+ - After `AuthError` import (if exists)
671
+ - After `MODULE_IMPORTS_END` comment (if exists)
672
+ - Before `MODULE_IMPORTS_START` comment (if exists)
673
+ - After the last import statement (fallback)
674
+
675
+ 2. **Route placement strategies:**
676
+ - Before `MODULE_ROUTES_END` comment (if exists)
677
+ - Before closing `</Routes>` tag
678
+ - After the last `<Route>` tag (fallback)
679
+
680
+ **Example - Before plugin:**
681
+
682
+ ```tsx
683
+ // App.tsx
684
+ import { Route, Routes } from 'react-router-dom';
685
+
686
+ export default function App() {
687
+ return (
688
+ <Routes>
689
+ <Route path="/" element={<Home />} />
690
+ <Route path="/about" element={<About />} />
691
+ </Routes>
692
+ );
693
+ }
694
+ ```
695
+
696
+ **Example - After plugin (automatically transformed):**
697
+
698
+ ```tsx
699
+ // App.tsx
700
+ import { Route, Routes } from 'react-router-dom';
701
+ import NotFoundPage from 'virtual:404-page';
702
+
703
+ export default function App() {
704
+ return (
705
+ <Routes>
706
+ <Route path="/" element={<Home />} />
707
+ <Route path="/about" element={<About />} />
708
+ <Route path="*" element={<NotFoundPage />} />
709
+ </Routes>
710
+ );
711
+ }
712
+ ```
713
+
714
+ **404 Page Features:**
715
+
716
+ The generated 404 page includes:
717
+ - Modern, responsive design with gradient background
718
+ - "Return Home" button
719
+ - "Create page" button (when running in MGX iframe) - uses `client.frame.createPage()`
720
+ - "Go to MGX" button (when not in iframe)
721
+
722
+ **Customization:**
723
+
724
+ If you need to customize the 404 page, you can:
725
+ 1. Create your own 404 component
726
+ 2. Import it manually in `App.tsx`
727
+ 3. The plugin will detect existing 404 routes and skip automatic addition
728
+
729
+ **Note:** The plugin only processes files that contain `App.tsx` in their path and end with `.tsx`. It will skip files that already have a 404 route configured.
730
+
731
+ ---
732
+
601
733
  ## TypeScript Support
602
734
 
603
735
  The SDK is written in TypeScript and provides full type definitions. You can use generic types for typed responses:
@@ -636,3 +768,38 @@ try {
636
768
  }
637
769
  }
638
770
  ```
771
+
772
+ ---
773
+
774
+ ## Exports
775
+
776
+ The SDK exports the following:
777
+
778
+ ### Main Exports
779
+
780
+ ```typescript
781
+ import { createClient, vitePlugin404 } from '@metagptx/web-sdk';
782
+ ```
783
+
784
+ - **`createClient(config?)`**: Creates a new SDK client instance
785
+ - **`vitePlugin404()`**: Vite plugin for automatic 404 page setup
786
+
787
+ ### Type Exports
788
+
789
+ ```typescript
790
+ import type {
791
+ AnyType,
792
+ ApiCallParams,
793
+ ClientConfig,
794
+ IntegrationFunction,
795
+ IntegrationParams,
796
+ RequestConfig,
797
+ } from '@metagptx/web-sdk';
798
+ ```
799
+
800
+ - **`ClientConfig`**: Configuration options for `createClient()`
801
+ - **`ApiCallParams`**: Parameters for `apiCall.invoke()`
802
+ - **`IntegrationFunction`**: Type for integration functions
803
+ - **`IntegrationParams`**: Parameters for integration function calls
804
+ - **`RequestConfig`**: Axios request configuration options
805
+ - **`AnyType`**: Generic type utility
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import e from"axios";import t from"qs";const n=e=>{let t=e.response?.status;if(t&&t>=400&&t!==401)try{typeof window<`u`&&window.top&&window.top?.postMessage({type:`mgx-appview-error`,targetName:window.name,data:{errMsg:e.response?.data?.message||e.response?.data?.detail||`Server Error`,stack:JSON.stringify({url:e.response?.config?.url,data:e.response?.data,status:e.response?.status})}},`*`)}catch(e){console.warn(`Failed to notify parent window about API error:`,e)}},r=(r={})=>{let{onUnauthorized:i,...a}=r,o=typeof globalThis<`u`&&`localStorage`in globalThis&&typeof globalThis.localStorage?.getItem==`function`?globalThis.localStorage.getItem(`token`)??void 0:void 0,s={timeout:6e4,paramsSerializer:e=>t.stringify(e,{arrayFormat:`brackets`}),...a,headers:{"Content-Type":`application/json`,"App-Host":globalThis?.window?.location?.origin??``,...o?{Authorization:`Bearer ${o}`}:{},...a.headers}},c=e.create(s);return c.interceptors.request.use(e=>e,e=>Promise.reject(e)),c.interceptors.response.use(e=>e,e=>{let t=e.response?.status;return t===401&&i&&i(),n(e),Promise.reject(e)}),c},i=[`stripe.com`],a=e=>typeof e==`string`?e.startsWith(`_`)||e.startsWith(`$`)||e===`constructor`||e===`toString`||e===`valueOf`||e===`inspect`||e===`toJSON`:!0,o=()=>{let e=window.location?.href??``,t=``;e.startsWith(`/`)?t=e:e&&(t=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i,``));let n=new URLSearchParams({from_url:t});window.location.href=`/api/v1/auth/login?${n.toString()}`},s=()=>globalThis?.window?.name?.includes(`devIframe`),c=e=>e?i.some(t=>e.toString().includes(t)):!1,l=e=>{let{requestInstance:t}=e;return{async login(){let e=new URLSearchParams(window.location.search),t=e.get(`token`);return t&&typeof globalThis<`u`&&`localStorage`in globalThis&&(globalThis.localStorage?.setItem(`token`,t),window.location.href=`/`),t},async me(){return t.get(`/api/v1/auth/me`)},async logout(){typeof globalThis<`u`&&`localStorage`in globalThis&&globalThis.localStorage?.removeItem(`token`);let e=await t.get(`/api/v1/auth/logout`);return typeof globalThis<`u`&&`window`in globalThis&&(globalThis.window.opener=null),window.location.href=`/`,e},toLogin:o}},u=e=>{let{requestInstance:t,entityName:n}=e,r=`/api/v1/entities/${n}`,i=e=>{if(!e)return;let t={...e};return e.fields&&Array.isArray(e.fields)&&(t.fields=e.fields.join(`,`)),e.query&&typeof e.query==`object`&&(t.query=JSON.stringify(e.query)),t};return{async query(e){let n=i(e);return t.get(r,{params:n})},async queryAll(e){let n=i(e);return t.get(`${r}/all`,{params:n})},async get(e){let{id:n,...a}=e,o=i(a);return t.get(`${r}/${n}`,{params:o})},async create(e){return t.post(r,e.data)},async update(e){return t.put(`${r}/${e.id}`,e.data)},async delete(e){return t.delete(`${r}/${e.id}`)},async deleteBatch(e){return t.delete(`${r}/batch`,{data:{ids:e.ids}})},async createBatch(e){return t.post(`${r}/batch`,e.data)},async updateBatch(e){return t.put(`${r}/batch`,e.data)}}},d=e=>{let{requestInstance:t}=e,n=new Map;return new Proxy({},{get(e,r){if(!a(r))return n.has(r)||n.set(r,u({requestInstance:t,entityName:r})),n.get(r)}})},f=e=>{let{requestInstance:t}=e;return{async invoke(e){let{url:n,method:r=`GET`,data:i,options:a={}}=e;if(typeof window<`u`&&a?.responseType===`stream`){let e;try{typeof globalThis<`u`&&`localStorage`in globalThis&&typeof globalThis.localStorage?.getItem==`function`&&(e=globalThis.localStorage.getItem(`token`)??void 0)}catch{}let t={"Content-Type":`application/json`,"App-Host":globalThis?.window?.location?.origin??``,...e?{Authorization:`Bearer ${e}`}:{},...a.headers||{}},o;i&&[`POST`,`PUT`,`PATCH`].includes(r.toUpperCase())&&(o=JSON.stringify(i));let s=await fetch(n,{method:r.toUpperCase(),headers:t,body:o});return s}let o={method:r.toUpperCase(),url:n,...a};return i&&[`POST`,`PUT`,`PATCH`].includes(o.method)?o.data=i:i&&[`GET`,`DELETE`].includes(o.method)&&(o.params=i),t.request(o)}}},p=e=>{let{requestInstance:t}=e;return new Proxy({},{get(e,n){if(!a(n))return new Proxy({},{get(e,r){if(!a(r))return(e={})=>{let i=`/api/integrations/core/${r}`;n!==`core`&&(i=`/api/integrations/providers/${n}/${r}`);let{payload:a={},option:o={}}=e,s=a instanceof FormData?{...o,headers:{...o.headers,"Content-Type":void 0}}:o;return t.post(i,a,s)}}})}})};let m=null;const h=e=>{if(m)return;let{requestInstance:t}=e,n=async e=>{let n=globalThis?.localStorage?.getItem(`token`);if(n)return;let r=e.token,i=await t.post(`/api/v1/auth/token/exchange`,{platform_token:r});i.status===200&&i.data.token&&(globalThis?.localStorage?.setItem(`token`,i.data.token),window.location.href=`/`)},r=e=>{switch(e.data.type){case`mgx-token-saved`:n(e.data.data);break;default:break}};m={requestInstance:t,handleMessage:r},globalThis?.window?.addEventListener(`message`,r)},g=()=>{let e=(e,t,n,r)=>{if(globalThis?.window===void 0||!globalThis?.window?.top){console.warn(`postMessage: window.top is not available`);return}try{let{targetOrigin:i=`*`,targetName:a=globalThis?.window?.name??``}=n||{},o={type:e,targetName:a,data:t};(r??globalThis?.window?.top)?.postMessage(o,i)}catch(e){console.warn(`Failed to send postMessage:`,e)}};return{postMessage:e}};let _=!1;const v=e=>{h(e);let{postMessage:t}=g(),n=()=>{if(_)return;_=!0;let e=globalThis?.localStorage?.getItem(`token`);e||(t(`mgx-token-request`,{domain:globalThis?.window?.location?.href??``},{},globalThis?.window?.opener),t(`mgx-token-request`,{domain:globalThis?.window?.location?.href??``}))};return n(),{createPage(e){let n=e??globalThis?.window?.location?.pathname??``;t(`mgx-create-page`,{path:n})}}},y=()=>{let{postMessage:e}=g(),t=t=>{if(t){if(globalThis?.window===void 0){console.warn(`openUrl: window is not available`);return}if(c(t)&&s()){e(`mgx-open-url`,{url:t.toString()},{},globalThis?.window?.opener);return}globalThis.window.location.href=t.toString()}};return{openUrl:t}},b=(e={})=>{let t=r({baseURL:`/`,...e}),n=l({requestInstance:t}),i=d({requestInstance:t}),a=f({requestInstance:t}),o=p({requestInstance:t}),s=v({requestInstance:t}),c=y();return{auth:n,entities:i,apiCall:a,integrations:o,frame:s,utils:c}};function x(){let e=`virtual:404-page`,t=`import { Button } from '@/components/ui/button';
1
+ import e from"axios";import t from"qs";const n=e=>{let t=e.response?.status;if(t&&t>=400&&t!==401)try{typeof window<`u`&&window.top&&window.top?.postMessage({type:`mgx-appview-error`,targetName:window.name,data:{errMsg:e.response?.data?.message||e.response?.data?.detail||`Server Error`,stack:JSON.stringify({url:e.response?.config?.url,data:e.response?.data,status:e.response?.status})}},`*`)}catch(e){console.warn(`Failed to notify parent window about API error:`,e)}},r=(r={})=>{let{onUnauthorized:i,...a}=r,o=typeof globalThis<`u`&&`localStorage`in globalThis&&typeof globalThis.localStorage?.getItem==`function`?globalThis.localStorage.getItem(`token`)??void 0:void 0,s={timeout:6e4,paramsSerializer:e=>t.stringify(e,{arrayFormat:`brackets`}),...a,headers:{"Content-Type":`application/json`,"App-Host":globalThis?.window?.location?.origin??``,...o?{Authorization:`Bearer ${o}`}:{},...a.headers}},c=e.create(s);return c.interceptors.request.use(e=>e,e=>Promise.reject(e)),c.interceptors.response.use(e=>e,e=>{let t=e.response?.status;return t===401&&i&&i(),n(e),Promise.reject(e)}),c},i=[`stripe.com`],a=e=>typeof e==`string`?e.startsWith(`_`)||e.startsWith(`$`)||e===`constructor`||e===`toString`||e===`valueOf`||e===`inspect`||e===`toJSON`:!0,o=()=>{let e=window.location?.href??``,t=``;e.startsWith(`/`)?t=e:e&&(t=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i,``));let n=new URLSearchParams({from_url:t});window.location.href=`/api/v1/auth/login?${n.toString()}`},s=()=>globalThis?.window?.name?.includes(`devIframe`),c=e=>e?i.some(t=>e.toString().includes(t)):!1,l=e=>{let{requestInstance:t}=e;return{async login(){let e=new URLSearchParams(window.location.search),t=e.get(`token`);return t&&typeof globalThis<`u`&&`localStorage`in globalThis&&(globalThis.localStorage?.setItem(`token`,t),globalThis.localStorage?.setItem(`isLougOutManual`,`false`),window.location.href=`/`),t},async me(){return t.get(`/api/v1/auth/me`)},async logout(){typeof globalThis<`u`&&`localStorage`in globalThis&&(globalThis.localStorage?.removeItem(`token`),globalThis.localStorage?.setItem(`isLougOutManual`,`true`));let e=await t.get(`/api/v1/auth/logout`);return typeof globalThis<`u`&&`window`in globalThis&&(globalThis.window.opener=null),window.location.href=`/`,e},toLogin:o}},u=e=>{let{requestInstance:t,entityName:n}=e,r=`/api/v1/entities/${n}`,i=e=>{if(!e)return;let t={...e};return e.fields&&Array.isArray(e.fields)&&(t.fields=e.fields.join(`,`)),e.query&&typeof e.query==`object`&&(t.query=JSON.stringify(e.query)),t};return{async query(e){let n=i(e);return t.get(r,{params:n})},async queryAll(e){let n=i(e);return t.get(`${r}/all`,{params:n})},async get(e){let{id:n,...a}=e,o=i(a);return t.get(`${r}/${n}`,{params:o})},async create(e){return t.post(r,e.data)},async update(e){return t.put(`${r}/${e.id}`,e.data)},async delete(e){return t.delete(`${r}/${e.id}`)},async deleteBatch(e){return t.delete(`${r}/batch`,{data:{ids:e.ids}})},async createBatch(e){return t.post(`${r}/batch`,e.data)},async updateBatch(e){return t.put(`${r}/batch`,e.data)}}},d=e=>{let{requestInstance:t}=e,n=new Map;return new Proxy({},{get(e,r){if(!a(r))return n.has(r)||n.set(r,u({requestInstance:t,entityName:r})),n.get(r)}})},f=e=>{let{requestInstance:t}=e;return{async invoke(e){let{url:n,method:r=`GET`,data:i,options:a={}}=e;if(typeof window<`u`&&a?.responseType===`stream`){let e;try{typeof globalThis<`u`&&`localStorage`in globalThis&&typeof globalThis.localStorage?.getItem==`function`&&(e=globalThis.localStorage.getItem(`token`)??void 0)}catch{}let t={"Content-Type":`application/json`,"App-Host":globalThis?.window?.location?.origin??``,...e?{Authorization:`Bearer ${e}`}:{},...a.headers||{}},o;i&&[`POST`,`PUT`,`PATCH`].includes(r.toUpperCase())&&(o=JSON.stringify(i));let s=await fetch(n,{method:r.toUpperCase(),headers:t,body:o});return s}let o={method:r.toUpperCase(),url:n,...a};return i&&[`POST`,`PUT`,`PATCH`].includes(o.method)?o.data=i:i&&[`GET`,`DELETE`].includes(o.method)&&(o.params=i),t.request(o)}}},p=e=>{let{requestInstance:t}=e;return new Proxy({},{get(e,n){if(!a(n))return new Proxy({},{get(e,r){if(!a(r))return(e={})=>{let i=`/api/integrations/core/${r}`;n!==`core`&&(i=`/api/integrations/providers/${n}/${r}`);let{payload:a={},option:o={}}=e,s=a instanceof FormData?{...o,headers:{...o.headers,"Content-Type":void 0}}:o;return t.post(i,a,s)}}})}})};let m=null;const h=e=>{if(m)return;let{requestInstance:t}=e,n=async e=>{let n=globalThis?.localStorage?.getItem(`token`);if(n)return;let r=e.token,i=await t.post(`/api/v1/auth/token/exchange`,{platform_token:r});i.status===200&&i.data.token&&(globalThis?.localStorage?.setItem(`token`,i.data.token),window.location.href=`/`)},r=e=>{switch(e.data.type){case`mgx-token-saved`:n(e.data.data);break;default:break}};m={requestInstance:t,handleMessage:r},globalThis?.window?.addEventListener(`message`,r)},g=()=>{let e=(e,t,n,r)=>{if(globalThis?.window===void 0||!globalThis?.window?.top){console.warn(`postMessage: window.top is not available`);return}try{let{targetOrigin:i=`*`,targetName:a=globalThis?.window?.name??``}=n||{},o={type:e,targetName:a,data:t};(r??globalThis?.window?.top)?.postMessage(o,i)}catch(e){console.warn(`Failed to send postMessage:`,e)}};return{postMessage:e}};let _=!1;const v=e=>{h(e);let{postMessage:t}=g(),n=()=>{if(_)return;_=!0;let e=globalThis?.localStorage?.getItem(`token`),n=globalThis?.localStorage?.getItem(`isLougOutManual`)===`true`,r=window.self!==window.top&&window.name?.includes(`devIframe`);!e&&!n&&r&&(t(`mgx-token-request`,{domain:globalThis?.window?.location?.href??``},{},globalThis?.window?.opener),t(`mgx-token-request`,{domain:globalThis?.window?.location?.href??``}))};return n(),{createPage(e){let n=e??globalThis?.window?.location?.pathname??``;t(`mgx-create-page`,{path:n})}}},y=()=>{let{postMessage:e}=g(),t=t=>{if(t){if(globalThis?.window===void 0){console.warn(`openUrl: window is not available`);return}if(c(t)&&s()){e(`mgx-open-url`,{url:t.toString()},{},globalThis?.window?.opener);return}globalThis.window.location.href=t.toString()}};return{openUrl:t}},b=(e={})=>{let t=r({baseURL:`/`,...e}),n=l({requestInstance:t}),i=d({requestInstance:t}),a=f({requestInstance:t}),o=p({requestInstance:t}),s=v({requestInstance:t}),c=y();return{auth:n,entities:i,apiCall:a,integrations:o,frame:s,utils:c}};function x(){let e=`virtual:404-page`,t=`import { Button } from '@/components/ui/button';
2
2
  import { createClient } from '@metagptx/web-sdk';
3
3
 
4
4
  // Create client instance
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@metagptx/web-sdk",
3
3
  "type": "module",
4
- "version": "0.0.49",
4
+ "version": "0.0.52-beta.3",
5
5
  "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b",
6
6
  "description": "TypeScript SDK for interacting with FuncSea API",
7
7
  "author": "MetaGPTX",
@@ -25,7 +25,7 @@
25
25
  "test": "vitest run",
26
26
  "test:coverage": "vitest --coverage run",
27
27
  "typecheck": "tsc --noEmit",
28
- "release": "bumpp && npm publish",
28
+ "release": "bumpp && npm publish --tag beta",
29
29
  "lint": "eslint",
30
30
  "lint:fix": "eslint --fix",
31
31
  "precommit": "npm run typecheck && lint-staged && npm run test:coverage",