@cloudparse/up-miniapps-sdk 0.1.0
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 +73 -0
- package/dist/bridge.d.ts +9 -0
- package/dist/index.browser.min.js +2 -0
- package/dist/index.browser.min.js.map +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +7 -0
- package/dist/sdk.d.ts +98 -0
- package/dist/types.d.ts +75 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# UP Mini-Apps SDK
|
|
2
|
+
|
|
3
|
+
The official SDK for building Mini-Apps within the UP Shell environment. This SDK provides a bridge between your web-based Mini-App and the native host shell.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install up-miniapps-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Initialization
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import sdk from 'up-miniapps-sdk';
|
|
17
|
+
|
|
18
|
+
// Or if using UMD/CDN
|
|
19
|
+
// const sdk = window.UPSDK;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### User Profile
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const profile = await sdk.user.getProfile(['fullName', 'email']);
|
|
26
|
+
console.log(profile.fullName);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Storage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// SQLite-backed storage isolated for your Mini-App
|
|
33
|
+
await sdk.storage.set('theme', 'dark');
|
|
34
|
+
const theme = await sdk.storage.get('theme');
|
|
35
|
+
|
|
36
|
+
// Hardware-level secure storage
|
|
37
|
+
await sdk.secureStore.set('api_token', 'xyz123');
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Location
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const position = await sdk.location.getCurrentPosition();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Payments
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const result = await sdk.payment.request({
|
|
50
|
+
amount: 10,
|
|
51
|
+
currency: 'USD',
|
|
52
|
+
total: 10,
|
|
53
|
+
items: [{ item: 'Coffee', price: 10, quantity: 1 }]
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Development
|
|
58
|
+
|
|
59
|
+
### Build
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run build
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Structure
|
|
66
|
+
|
|
67
|
+
- `src/bridge.ts`: Communication layer with the Native Shell.
|
|
68
|
+
- `src/sdk.ts`: Main SDK entry point and module composition.
|
|
69
|
+
- `src/types.ts`: TypeScript definitions.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class Bridge {
|
|
2
|
+
private callbacks;
|
|
3
|
+
private eventListeners;
|
|
4
|
+
constructor();
|
|
5
|
+
private handleNativeMessage;
|
|
6
|
+
on(event: string, callback: (data: unknown) => void): () => void;
|
|
7
|
+
off(event: string, callback: (data: unknown) => void): void;
|
|
8
|
+
send<T = unknown>(type: string, payload?: unknown): Promise<T>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var UPSDK=(()=>{var l=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var p=(r,i)=>{for(var e in i)l(r,e,{get:i[e],enumerable:!0})},h=(r,i,e,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of b(i))!f.call(r,t)&&t!==e&&l(r,t,{get:()=>i[t],enumerable:!(s=u(i,t))||s.enumerable});return r};var E=r=>h(l({},"__esModule",{value:!0}),r);var P={};p(P,{MiniAppSDK:()=>n,default:()=>m,sdk:()=>a});var o=class{callbacks={};eventListeners={};constructor(){typeof window<"u"&&(window.__MiniAppBridge={onNativeMessage:i=>this.handleNativeMessage(i)},window.addEventListener("message",i=>{try{let e=typeof i.data=="string"?JSON.parse(i.data):i.data;this.handleNativeMessage(e)}catch{}}))}handleNativeMessage(i){if(!(!i||typeof i!="object")){if(i.callbackId&&typeof i.callbackId=="string"&&this.callbacks[i.callbackId]){let{callbackId:e}=i,{resolve:s,reject:t}=this.callbacks[e];i.success===!1||i.error?t(i.error||"Unknown error"):s(i.value!==void 0?i.value:i.payload||i),delete this.callbacks[e];return}if(i.event&&typeof i.event=="string"){let e=this.eventListeners[i.event];if(e)for(let s of e)try{s(i.payload||i.value)}catch(t){console.error(`Error in event listener for ${i.event}:`,t)}}}}on(i,e){return this.eventListeners[i]||(this.eventListeners[i]=[]),this.eventListeners[i].push(e),()=>this.off(i,e)}off(i,e){this.eventListeners[i]&&(this.eventListeners[i]=this.eventListeners[i].filter(s=>s!==e))}send(i,e){return new Promise((s,t)=>{let d=`${i}_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;this.callbacks[d]={reject:t,resolve:s};let g={callbackId:d,payload:e,type:i},c=window;c.ReactNativeWebView?.postMessage?c.ReactNativeWebView.postMessage(JSON.stringify(g)):(console.warn("UP Bridge not available. Are you running inside the UP Shell?"),delete this.callbacks[d],t(new Error("Bridge not available")))})}};var n=class{bridge;constructor(){this.bridge=new o}storage={get:i=>this.bridge.send("STORAGE_GET",{key:i}),getAll:()=>this.bridge.send("STORAGE_GET_ALL"),set:(i,e)=>this.bridge.send("STORAGE_SET",{key:i,value:e})};secureStore={get:i=>this.bridge.send("SECURE_GET",{key:i}),set:(i,e)=>this.bridge.send("SECURE_SET",{key:i,value:e})};location={getCurrentPosition:()=>this.bridge.send("GET_LOCATION")};user={getIdentityToken:i=>this.bridge.send("GET_IDENTITY_TOKEN",{scopes:i}),getProfile:i=>this.bridge.send("GET_USER_PROFILE",{fields:i})};permission={request:(i,e)=>this.bridge.send("REQUEST_PERMISSION",{fields:e,type:i})};payment={request:i=>this.bridge.send("PAYMENT_REQUEST",i)};activity={start:(i,e,s)=>this.bridge.send("START_ACTIVITY",{id:i,status:s,title:e})};haptic={trigger:i=>this.bridge.send("TRIGGER_HAPTIC",{style:i})};network={request:i=>this.bridge.send("NETWORK_REQUEST",i)};fidelity={getFidelityBalance:()=>this.bridge.send("GET_FIDELITY_BALANCE"),getFidelityHistory:i=>this.bridge.send("GET_FIDELITY_HISTORY",i),spendFPoints:i=>this.bridge.send("SPEND_FPOINTS",{intentUuid:i})};ui={exit:()=>this.bridge.send("EXIT")};on(i,e){return this.bridge.on(i,e)}off(i,e){this.bridge.off(i,e)}},a=new n;var m=a;return E(P);})();
|
|
2
|
+
//# sourceMappingURL=index.browser.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/bridge.ts", "../src/sdk.ts"],
|
|
4
|
+
"sourcesContent": ["export { MiniAppSDK, sdk } from \"@sdk/sdk\";\nexport * from \"@sdk/types\";\n\nimport { sdk } from \"@sdk/sdk\";\nexport default sdk;\n", "import type { BridgeEnvelope } from \"@sdk/types\";\n\nexport class Bridge {\n private callbacks: Record<\n string,\n { resolve: (val: unknown) => void; reject: (err: unknown) => void }\n > = {};\n private eventListeners: Record<string, ((data: unknown) => void)[]> = {};\n\n constructor() {\n if (typeof window !== \"undefined\") {\n // Set up the global bridge receiver for React Native WebView\n (window as unknown as { __MiniAppBridge: unknown }).__MiniAppBridge = {\n onNativeMessage: (envelope: BridgeEnvelope) =>\n this.handleNativeMessage(envelope),\n };\n\n // Also listen for postMessage events\n window.addEventListener(\"message\", (event) => {\n try {\n const data =\n typeof event.data === \"string\"\n ? JSON.parse(event.data)\n : event.data;\n this.handleNativeMessage(data);\n } catch (_e) {\n // Ignore non-JSON messages\n }\n });\n }\n }\n\n private handleNativeMessage(envelope: BridgeEnvelope) {\n if (!envelope || typeof envelope !== \"object\") return;\n\n // 1. Handle Response Callbacks\n if (\n envelope.callbackId &&\n typeof envelope.callbackId === \"string\" &&\n this.callbacks[envelope.callbackId]\n ) {\n const { callbackId } = envelope;\n const { resolve, reject } = this.callbacks[callbackId];\n\n if (envelope.success === false || envelope.error) {\n reject(envelope.error || \"Unknown error\");\n } else {\n resolve(\n envelope.value !== undefined\n ? envelope.value\n : envelope.payload || envelope,\n );\n }\n\n delete this.callbacks[callbackId];\n return;\n }\n\n // 2. Handle Pushed Events\n if (envelope.event && typeof envelope.event === \"string\") {\n const listeners = this.eventListeners[envelope.event];\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(envelope.payload || envelope.value);\n } catch (e) {\n console.error(`Error in event listener for ${envelope.event}:`, e);\n }\n }\n }\n }\n }\n\n public on(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) {\n this.eventListeners[event] = [];\n }\n this.eventListeners[event].push(callback);\n return () => this.off(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) return;\n this.eventListeners[event] = this.eventListeners[event].filter(\n (cb) => cb !== callback,\n );\n }\n\n public send<T = unknown>(type: string, payload?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n this.callbacks[callbackId] = {\n reject,\n resolve: resolve as (val: unknown) => void,\n };\n\n const message = {\n callbackId,\n payload,\n type,\n };\n\n const win = window as unknown as {\n ReactNativeWebView?: { postMessage: (msg: string) => void };\n };\n\n if (win.ReactNativeWebView?.postMessage) {\n win.ReactNativeWebView.postMessage(JSON.stringify(message));\n } else {\n console.warn(\n \"UP Bridge not available. Are you running inside the UP Shell?\",\n );\n delete this.callbacks[callbackId];\n reject(new Error(\"Bridge not available\"));\n }\n });\n }\n}\n", "import { Bridge } from \"@sdk/bridge\";\nimport type {\n FidelityBalance,\n FidelityHistoryParams,\n FidelityTransaction,\n HapticStyle,\n LocationData,\n NetworkOptions,\n NetworkResponse,\n PaymentRequest,\n UserProfile,\n} from \"@sdk/types\";\n\nexport class MiniAppSDK {\n private bridge: Bridge;\n\n constructor() {\n this.bridge = new Bridge();\n }\n\n /**\n * Local SQLite Storage (Isolated per Mini-App)\n */\n public storage = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"STORAGE_GET\", { key }),\n getAll: (): Promise<Record<string, string>> =>\n this.bridge.send(\"STORAGE_GET_ALL\"),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"STORAGE_SET\", { key, value }),\n };\n\n /**\n * Hardware-level Secure Storage (Isolated per Mini-App)\n */\n public secureStore = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"SECURE_GET\", { key }),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"SECURE_SET\", { key, value }),\n };\n\n /**\n * Device Location\n */\n public location = {\n getCurrentPosition: (): Promise<LocationData> =>\n this.bridge.send(\"GET_LOCATION\"),\n };\n\n /**\n * Identity & Profile\n */\n public user = {\n /**\n * Fetch a short-lived Scoped Identity Token (JWT) signed by the UP Hub.\n */\n getIdentityToken: (scopes?: string[]): Promise<string> =>\n this.bridge.send(\"GET_IDENTITY_TOKEN\", { scopes }),\n /**\n * Fetch user profile data.\n */\n getProfile: <T extends keyof UserProfile>(\n fields?: T[],\n ): Promise<Partial<UserProfile>> =>\n this.bridge.send(\"GET_USER_PROFILE\", { fields }),\n };\n\n /**\n * Explicit Permission Management\n */\n public permission = {\n request: ((type: \"location\" | \"profile\", fields?: string[]) => {\n return this.bridge.send(\"REQUEST_PERMISSION\", { fields, type });\n }) as {\n (type: \"location\"): Promise<boolean>;\n (type: \"profile\", fields: (keyof UserProfile)[]): Promise<boolean>;\n },\n };\n\n /**\n * Payments\n */\n public payment = {\n request: (\n payload: PaymentRequest,\n ): Promise<{ success: true; transactionId: string }> =>\n this.bridge.send(\"PAYMENT_REQUEST\", payload),\n };\n\n /**\n * Live Activities & Notifications\n */\n public activity = {\n start: (id: string, title: string, status: string): Promise<void> =>\n this.bridge.send(\"START_ACTIVITY\", { id, status, title }),\n };\n\n /**\n * Haptics\n */\n public haptic = {\n trigger: (style: HapticStyle): Promise<void> =>\n this.bridge.send(\"TRIGGER_HAPTIC\", { style }),\n };\n\n /**\n * Network Proxy\n */\n public network = {\n request: (options: NetworkOptions): Promise<NetworkResponse> =>\n this.bridge.send(\"NETWORK_REQUEST\", options),\n };\n\n /**\n * Fidelity & Rewards\n */\n public fidelity = {\n getFidelityBalance: (): Promise<FidelityBalance> =>\n this.bridge.send(\"GET_FIDELITY_BALANCE\"),\n getFidelityHistory: (\n params?: FidelityHistoryParams,\n ): Promise<FidelityTransaction[]> =>\n this.bridge.send(\"GET_FIDELITY_HISTORY\", params),\n spendFPoints: (\n intentUuid: string,\n ): Promise<{ success: boolean; transactionId: string }> =>\n this.bridge.send(\"SPEND_FPOINTS\", { intentUuid }),\n };\n\n /**\n * Navigation & UI\n */\n public ui = {\n exit: (): Promise<void> => this.bridge.send(\"EXIT\"),\n };\n\n /**\n * Event Listeners\n */\n public on(event: string, callback: (data: unknown) => void) {\n return this.bridge.on(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n this.bridge.off(event, callback);\n }\n}\n\nexport const sdk = new MiniAppSDK();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["src_exports", "__export", "MiniAppSDK", "src_default", "sdk", "Bridge", "envelope", "event", "data", "callbackId", "resolve", "reject", "listeners", "listener", "e", "callback", "cb", "type", "payload", "message", "win", "MiniAppSDK", "Bridge", "key", "value", "scopes", "fields", "type", "payload", "id", "title", "status", "style", "options", "params", "intentUuid", "event", "callback", "sdk", "src_default", "sdk"]
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var l=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var p=(r,i)=>{for(var e in i)l(r,e,{get:i[e],enumerable:!0})},h=(r,i,e,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of b(i))!f.call(r,t)&&t!==e&&l(r,t,{get:()=>i[t],enumerable:!(s=u(i,t))||s.enumerable});return r};var E=r=>h(l({},"__esModule",{value:!0}),r);var P={};p(P,{MiniAppSDK:()=>n,default:()=>m,sdk:()=>a});module.exports=E(P);var o=class{callbacks={};eventListeners={};constructor(){typeof window<"u"&&(window.__MiniAppBridge={onNativeMessage:i=>this.handleNativeMessage(i)},window.addEventListener("message",i=>{try{let e=typeof i.data=="string"?JSON.parse(i.data):i.data;this.handleNativeMessage(e)}catch{}}))}handleNativeMessage(i){if(!(!i||typeof i!="object")){if(i.callbackId&&typeof i.callbackId=="string"&&this.callbacks[i.callbackId]){let{callbackId:e}=i,{resolve:s,reject:t}=this.callbacks[e];i.success===!1||i.error?t(i.error||"Unknown error"):s(i.value!==void 0?i.value:i.payload||i),delete this.callbacks[e];return}if(i.event&&typeof i.event=="string"){let e=this.eventListeners[i.event];if(e)for(let s of e)try{s(i.payload||i.value)}catch(t){console.error(`Error in event listener for ${i.event}:`,t)}}}}on(i,e){return this.eventListeners[i]||(this.eventListeners[i]=[]),this.eventListeners[i].push(e),()=>this.off(i,e)}off(i,e){this.eventListeners[i]&&(this.eventListeners[i]=this.eventListeners[i].filter(s=>s!==e))}send(i,e){return new Promise((s,t)=>{let d=`${i}_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;this.callbacks[d]={reject:t,resolve:s};let g={callbackId:d,payload:e,type:i},c=window;c.ReactNativeWebView?.postMessage?c.ReactNativeWebView.postMessage(JSON.stringify(g)):(console.warn("UP Bridge not available. Are you running inside the UP Shell?"),delete this.callbacks[d],t(new Error("Bridge not available")))})}};var n=class{bridge;constructor(){this.bridge=new o}storage={get:i=>this.bridge.send("STORAGE_GET",{key:i}),getAll:()=>this.bridge.send("STORAGE_GET_ALL"),set:(i,e)=>this.bridge.send("STORAGE_SET",{key:i,value:e})};secureStore={get:i=>this.bridge.send("SECURE_GET",{key:i}),set:(i,e)=>this.bridge.send("SECURE_SET",{key:i,value:e})};location={getCurrentPosition:()=>this.bridge.send("GET_LOCATION")};user={getIdentityToken:i=>this.bridge.send("GET_IDENTITY_TOKEN",{scopes:i}),getProfile:i=>this.bridge.send("GET_USER_PROFILE",{fields:i})};permission={request:(i,e)=>this.bridge.send("REQUEST_PERMISSION",{fields:e,type:i})};payment={request:i=>this.bridge.send("PAYMENT_REQUEST",i)};activity={start:(i,e,s)=>this.bridge.send("START_ACTIVITY",{id:i,status:s,title:e})};haptic={trigger:i=>this.bridge.send("TRIGGER_HAPTIC",{style:i})};network={request:i=>this.bridge.send("NETWORK_REQUEST",i)};fidelity={getFidelityBalance:()=>this.bridge.send("GET_FIDELITY_BALANCE"),getFidelityHistory:i=>this.bridge.send("GET_FIDELITY_HISTORY",i),spendFPoints:i=>this.bridge.send("SPEND_FPOINTS",{intentUuid:i})};ui={exit:()=>this.bridge.send("EXIT")};on(i,e){return this.bridge.on(i,e)}off(i,e){this.bridge.off(i,e)}},a=new n;var m=a;0&&(module.exports={MiniAppSDK,sdk});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/bridge.ts", "../src/sdk.ts"],
|
|
4
|
+
"sourcesContent": ["export { MiniAppSDK, sdk } from \"@sdk/sdk\";\nexport * from \"@sdk/types\";\n\nimport { sdk } from \"@sdk/sdk\";\nexport default sdk;\n", "import type { BridgeEnvelope } from \"@sdk/types\";\n\nexport class Bridge {\n private callbacks: Record<\n string,\n { resolve: (val: unknown) => void; reject: (err: unknown) => void }\n > = {};\n private eventListeners: Record<string, ((data: unknown) => void)[]> = {};\n\n constructor() {\n if (typeof window !== \"undefined\") {\n // Set up the global bridge receiver for React Native WebView\n (window as unknown as { __MiniAppBridge: unknown }).__MiniAppBridge = {\n onNativeMessage: (envelope: BridgeEnvelope) =>\n this.handleNativeMessage(envelope),\n };\n\n // Also listen for postMessage events\n window.addEventListener(\"message\", (event) => {\n try {\n const data =\n typeof event.data === \"string\"\n ? JSON.parse(event.data)\n : event.data;\n this.handleNativeMessage(data);\n } catch (_e) {\n // Ignore non-JSON messages\n }\n });\n }\n }\n\n private handleNativeMessage(envelope: BridgeEnvelope) {\n if (!envelope || typeof envelope !== \"object\") return;\n\n // 1. Handle Response Callbacks\n if (\n envelope.callbackId &&\n typeof envelope.callbackId === \"string\" &&\n this.callbacks[envelope.callbackId]\n ) {\n const { callbackId } = envelope;\n const { resolve, reject } = this.callbacks[callbackId];\n\n if (envelope.success === false || envelope.error) {\n reject(envelope.error || \"Unknown error\");\n } else {\n resolve(\n envelope.value !== undefined\n ? envelope.value\n : envelope.payload || envelope,\n );\n }\n\n delete this.callbacks[callbackId];\n return;\n }\n\n // 2. Handle Pushed Events\n if (envelope.event && typeof envelope.event === \"string\") {\n const listeners = this.eventListeners[envelope.event];\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(envelope.payload || envelope.value);\n } catch (e) {\n console.error(`Error in event listener for ${envelope.event}:`, e);\n }\n }\n }\n }\n }\n\n public on(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) {\n this.eventListeners[event] = [];\n }\n this.eventListeners[event].push(callback);\n return () => this.off(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) return;\n this.eventListeners[event] = this.eventListeners[event].filter(\n (cb) => cb !== callback,\n );\n }\n\n public send<T = unknown>(type: string, payload?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n this.callbacks[callbackId] = {\n reject,\n resolve: resolve as (val: unknown) => void,\n };\n\n const message = {\n callbackId,\n payload,\n type,\n };\n\n const win = window as unknown as {\n ReactNativeWebView?: { postMessage: (msg: string) => void };\n };\n\n if (win.ReactNativeWebView?.postMessage) {\n win.ReactNativeWebView.postMessage(JSON.stringify(message));\n } else {\n console.warn(\n \"UP Bridge not available. Are you running inside the UP Shell?\",\n );\n delete this.callbacks[callbackId];\n reject(new Error(\"Bridge not available\"));\n }\n });\n }\n}\n", "import { Bridge } from \"@sdk/bridge\";\nimport type {\n FidelityBalance,\n FidelityHistoryParams,\n FidelityTransaction,\n HapticStyle,\n LocationData,\n NetworkOptions,\n NetworkResponse,\n PaymentRequest,\n UserProfile,\n} from \"@sdk/types\";\n\nexport class MiniAppSDK {\n private bridge: Bridge;\n\n constructor() {\n this.bridge = new Bridge();\n }\n\n /**\n * Local SQLite Storage (Isolated per Mini-App)\n */\n public storage = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"STORAGE_GET\", { key }),\n getAll: (): Promise<Record<string, string>> =>\n this.bridge.send(\"STORAGE_GET_ALL\"),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"STORAGE_SET\", { key, value }),\n };\n\n /**\n * Hardware-level Secure Storage (Isolated per Mini-App)\n */\n public secureStore = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"SECURE_GET\", { key }),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"SECURE_SET\", { key, value }),\n };\n\n /**\n * Device Location\n */\n public location = {\n getCurrentPosition: (): Promise<LocationData> =>\n this.bridge.send(\"GET_LOCATION\"),\n };\n\n /**\n * Identity & Profile\n */\n public user = {\n /**\n * Fetch a short-lived Scoped Identity Token (JWT) signed by the UP Hub.\n */\n getIdentityToken: (scopes?: string[]): Promise<string> =>\n this.bridge.send(\"GET_IDENTITY_TOKEN\", { scopes }),\n /**\n * Fetch user profile data.\n */\n getProfile: <T extends keyof UserProfile>(\n fields?: T[],\n ): Promise<Partial<UserProfile>> =>\n this.bridge.send(\"GET_USER_PROFILE\", { fields }),\n };\n\n /**\n * Explicit Permission Management\n */\n public permission = {\n request: ((type: \"location\" | \"profile\", fields?: string[]) => {\n return this.bridge.send(\"REQUEST_PERMISSION\", { fields, type });\n }) as {\n (type: \"location\"): Promise<boolean>;\n (type: \"profile\", fields: (keyof UserProfile)[]): Promise<boolean>;\n },\n };\n\n /**\n * Payments\n */\n public payment = {\n request: (\n payload: PaymentRequest,\n ): Promise<{ success: true; transactionId: string }> =>\n this.bridge.send(\"PAYMENT_REQUEST\", payload),\n };\n\n /**\n * Live Activities & Notifications\n */\n public activity = {\n start: (id: string, title: string, status: string): Promise<void> =>\n this.bridge.send(\"START_ACTIVITY\", { id, status, title }),\n };\n\n /**\n * Haptics\n */\n public haptic = {\n trigger: (style: HapticStyle): Promise<void> =>\n this.bridge.send(\"TRIGGER_HAPTIC\", { style }),\n };\n\n /**\n * Network Proxy\n */\n public network = {\n request: (options: NetworkOptions): Promise<NetworkResponse> =>\n this.bridge.send(\"NETWORK_REQUEST\", options),\n };\n\n /**\n * Fidelity & Rewards\n */\n public fidelity = {\n getFidelityBalance: (): Promise<FidelityBalance> =>\n this.bridge.send(\"GET_FIDELITY_BALANCE\"),\n getFidelityHistory: (\n params?: FidelityHistoryParams,\n ): Promise<FidelityTransaction[]> =>\n this.bridge.send(\"GET_FIDELITY_HISTORY\", params),\n spendFPoints: (\n intentUuid: string,\n ): Promise<{ success: boolean; transactionId: string }> =>\n this.bridge.send(\"SPEND_FPOINTS\", { intentUuid }),\n };\n\n /**\n * Navigation & UI\n */\n public ui = {\n exit: (): Promise<void> => this.bridge.send(\"EXIT\"),\n };\n\n /**\n * Event Listeners\n */\n public on(event: string, callback: (data: unknown) => void) {\n return this.bridge.on(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n this.bridge.off(event, callback);\n }\n}\n\nexport const sdk = new MiniAppSDK();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["src_exports", "__export", "MiniAppSDK", "src_default", "sdk", "__toCommonJS", "Bridge", "envelope", "event", "data", "callbackId", "resolve", "reject", "listeners", "listener", "e", "callback", "cb", "type", "payload", "message", "win", "MiniAppSDK", "Bridge", "key", "value", "scopes", "fields", "type", "payload", "id", "title", "status", "style", "options", "params", "intentUuid", "event", "callback", "sdk", "src_default", "sdk"]
|
|
7
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var r=class{callbacks={};eventListeners={};constructor(){typeof window<"u"&&(window.__MiniAppBridge={onNativeMessage:i=>this.handleNativeMessage(i)},window.addEventListener("message",i=>{try{let e=typeof i.data=="string"?JSON.parse(i.data):i.data;this.handleNativeMessage(e)}catch{}}))}handleNativeMessage(i){if(!(!i||typeof i!="object")){if(i.callbackId&&typeof i.callbackId=="string"&&this.callbacks[i.callbackId]){let{callbackId:e}=i,{resolve:s,reject:t}=this.callbacks[e];i.success===!1||i.error?t(i.error||"Unknown error"):s(i.value!==void 0?i.value:i.payload||i),delete this.callbacks[e];return}if(i.event&&typeof i.event=="string"){let e=this.eventListeners[i.event];if(e)for(let s of e)try{s(i.payload||i.value)}catch(t){console.error(`Error in event listener for ${i.event}:`,t)}}}}on(i,e){return this.eventListeners[i]||(this.eventListeners[i]=[]),this.eventListeners[i].push(e),()=>this.off(i,e)}off(i,e){this.eventListeners[i]&&(this.eventListeners[i]=this.eventListeners[i].filter(s=>s!==e))}send(i,e){return new Promise((s,t)=>{let o=`${i}_${Date.now()}_${Math.random().toString(36).substr(2,9)}`;this.callbacks[o]={reject:t,resolve:s};let c={callbackId:o,payload:e,type:i},d=window;d.ReactNativeWebView?.postMessage?d.ReactNativeWebView.postMessage(JSON.stringify(c)):(console.warn("UP Bridge not available. Are you running inside the UP Shell?"),delete this.callbacks[o],t(new Error("Bridge not available")))})}};var n=class{bridge;constructor(){this.bridge=new r}storage={get:i=>this.bridge.send("STORAGE_GET",{key:i}),getAll:()=>this.bridge.send("STORAGE_GET_ALL"),set:(i,e)=>this.bridge.send("STORAGE_SET",{key:i,value:e})};secureStore={get:i=>this.bridge.send("SECURE_GET",{key:i}),set:(i,e)=>this.bridge.send("SECURE_SET",{key:i,value:e})};location={getCurrentPosition:()=>this.bridge.send("GET_LOCATION")};user={getIdentityToken:i=>this.bridge.send("GET_IDENTITY_TOKEN",{scopes:i}),getProfile:i=>this.bridge.send("GET_USER_PROFILE",{fields:i})};permission={request:(i,e)=>this.bridge.send("REQUEST_PERMISSION",{fields:e,type:i})};payment={request:i=>this.bridge.send("PAYMENT_REQUEST",i)};activity={start:(i,e,s)=>this.bridge.send("START_ACTIVITY",{id:i,status:s,title:e})};haptic={trigger:i=>this.bridge.send("TRIGGER_HAPTIC",{style:i})};network={request:i=>this.bridge.send("NETWORK_REQUEST",i)};fidelity={getFidelityBalance:()=>this.bridge.send("GET_FIDELITY_BALANCE"),getFidelityHistory:i=>this.bridge.send("GET_FIDELITY_HISTORY",i),spendFPoints:i=>this.bridge.send("SPEND_FPOINTS",{intentUuid:i})};ui={exit:()=>this.bridge.send("EXIT")};on(i,e){return this.bridge.on(i,e)}off(i,e){this.bridge.off(i,e)}},a=new n;var p=a;export{n as MiniAppSDK,p as default,a as sdk};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/bridge.ts", "../src/sdk.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type { BridgeEnvelope } from \"@sdk/types\";\n\nexport class Bridge {\n private callbacks: Record<\n string,\n { resolve: (val: unknown) => void; reject: (err: unknown) => void }\n > = {};\n private eventListeners: Record<string, ((data: unknown) => void)[]> = {};\n\n constructor() {\n if (typeof window !== \"undefined\") {\n // Set up the global bridge receiver for React Native WebView\n (window as unknown as { __MiniAppBridge: unknown }).__MiniAppBridge = {\n onNativeMessage: (envelope: BridgeEnvelope) =>\n this.handleNativeMessage(envelope),\n };\n\n // Also listen for postMessage events\n window.addEventListener(\"message\", (event) => {\n try {\n const data =\n typeof event.data === \"string\"\n ? JSON.parse(event.data)\n : event.data;\n this.handleNativeMessage(data);\n } catch (_e) {\n // Ignore non-JSON messages\n }\n });\n }\n }\n\n private handleNativeMessage(envelope: BridgeEnvelope) {\n if (!envelope || typeof envelope !== \"object\") return;\n\n // 1. Handle Response Callbacks\n if (\n envelope.callbackId &&\n typeof envelope.callbackId === \"string\" &&\n this.callbacks[envelope.callbackId]\n ) {\n const { callbackId } = envelope;\n const { resolve, reject } = this.callbacks[callbackId];\n\n if (envelope.success === false || envelope.error) {\n reject(envelope.error || \"Unknown error\");\n } else {\n resolve(\n envelope.value !== undefined\n ? envelope.value\n : envelope.payload || envelope,\n );\n }\n\n delete this.callbacks[callbackId];\n return;\n }\n\n // 2. Handle Pushed Events\n if (envelope.event && typeof envelope.event === \"string\") {\n const listeners = this.eventListeners[envelope.event];\n if (listeners) {\n for (const listener of listeners) {\n try {\n listener(envelope.payload || envelope.value);\n } catch (e) {\n console.error(`Error in event listener for ${envelope.event}:`, e);\n }\n }\n }\n }\n }\n\n public on(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) {\n this.eventListeners[event] = [];\n }\n this.eventListeners[event].push(callback);\n return () => this.off(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n if (!this.eventListeners[event]) return;\n this.eventListeners[event] = this.eventListeners[event].filter(\n (cb) => cb !== callback,\n );\n }\n\n public send<T = unknown>(type: string, payload?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n this.callbacks[callbackId] = {\n reject,\n resolve: resolve as (val: unknown) => void,\n };\n\n const message = {\n callbackId,\n payload,\n type,\n };\n\n const win = window as unknown as {\n ReactNativeWebView?: { postMessage: (msg: string) => void };\n };\n\n if (win.ReactNativeWebView?.postMessage) {\n win.ReactNativeWebView.postMessage(JSON.stringify(message));\n } else {\n console.warn(\n \"UP Bridge not available. Are you running inside the UP Shell?\",\n );\n delete this.callbacks[callbackId];\n reject(new Error(\"Bridge not available\"));\n }\n });\n }\n}\n", "import { Bridge } from \"@sdk/bridge\";\nimport type {\n FidelityBalance,\n FidelityHistoryParams,\n FidelityTransaction,\n HapticStyle,\n LocationData,\n NetworkOptions,\n NetworkResponse,\n PaymentRequest,\n UserProfile,\n} from \"@sdk/types\";\n\nexport class MiniAppSDK {\n private bridge: Bridge;\n\n constructor() {\n this.bridge = new Bridge();\n }\n\n /**\n * Local SQLite Storage (Isolated per Mini-App)\n */\n public storage = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"STORAGE_GET\", { key }),\n getAll: (): Promise<Record<string, string>> =>\n this.bridge.send(\"STORAGE_GET_ALL\"),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"STORAGE_SET\", { key, value }),\n };\n\n /**\n * Hardware-level Secure Storage (Isolated per Mini-App)\n */\n public secureStore = {\n get: (key: string): Promise<string | null> =>\n this.bridge.send(\"SECURE_GET\", { key }),\n set: (key: string, value: string): Promise<void> =>\n this.bridge.send(\"SECURE_SET\", { key, value }),\n };\n\n /**\n * Device Location\n */\n public location = {\n getCurrentPosition: (): Promise<LocationData> =>\n this.bridge.send(\"GET_LOCATION\"),\n };\n\n /**\n * Identity & Profile\n */\n public user = {\n /**\n * Fetch a short-lived Scoped Identity Token (JWT) signed by the UP Hub.\n */\n getIdentityToken: (scopes?: string[]): Promise<string> =>\n this.bridge.send(\"GET_IDENTITY_TOKEN\", { scopes }),\n /**\n * Fetch user profile data.\n */\n getProfile: <T extends keyof UserProfile>(\n fields?: T[],\n ): Promise<Partial<UserProfile>> =>\n this.bridge.send(\"GET_USER_PROFILE\", { fields }),\n };\n\n /**\n * Explicit Permission Management\n */\n public permission = {\n request: ((type: \"location\" | \"profile\", fields?: string[]) => {\n return this.bridge.send(\"REQUEST_PERMISSION\", { fields, type });\n }) as {\n (type: \"location\"): Promise<boolean>;\n (type: \"profile\", fields: (keyof UserProfile)[]): Promise<boolean>;\n },\n };\n\n /**\n * Payments\n */\n public payment = {\n request: (\n payload: PaymentRequest,\n ): Promise<{ success: true; transactionId: string }> =>\n this.bridge.send(\"PAYMENT_REQUEST\", payload),\n };\n\n /**\n * Live Activities & Notifications\n */\n public activity = {\n start: (id: string, title: string, status: string): Promise<void> =>\n this.bridge.send(\"START_ACTIVITY\", { id, status, title }),\n };\n\n /**\n * Haptics\n */\n public haptic = {\n trigger: (style: HapticStyle): Promise<void> =>\n this.bridge.send(\"TRIGGER_HAPTIC\", { style }),\n };\n\n /**\n * Network Proxy\n */\n public network = {\n request: (options: NetworkOptions): Promise<NetworkResponse> =>\n this.bridge.send(\"NETWORK_REQUEST\", options),\n };\n\n /**\n * Fidelity & Rewards\n */\n public fidelity = {\n getFidelityBalance: (): Promise<FidelityBalance> =>\n this.bridge.send(\"GET_FIDELITY_BALANCE\"),\n getFidelityHistory: (\n params?: FidelityHistoryParams,\n ): Promise<FidelityTransaction[]> =>\n this.bridge.send(\"GET_FIDELITY_HISTORY\", params),\n spendFPoints: (\n intentUuid: string,\n ): Promise<{ success: boolean; transactionId: string }> =>\n this.bridge.send(\"SPEND_FPOINTS\", { intentUuid }),\n };\n\n /**\n * Navigation & UI\n */\n public ui = {\n exit: (): Promise<void> => this.bridge.send(\"EXIT\"),\n };\n\n /**\n * Event Listeners\n */\n public on(event: string, callback: (data: unknown) => void) {\n return this.bridge.on(event, callback);\n }\n\n public off(event: string, callback: (data: unknown) => void) {\n this.bridge.off(event, callback);\n }\n}\n\nexport const sdk = new MiniAppSDK();\n", "export { MiniAppSDK, sdk } from \"@sdk/sdk\";\nexport * from \"@sdk/types\";\n\nimport { sdk } from \"@sdk/sdk\";\nexport default sdk;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Bridge", "envelope", "event", "data", "callbackId", "resolve", "reject", "listeners", "listener", "e", "callback", "cb", "type", "payload", "message", "win", "MiniAppSDK", "Bridge", "key", "value", "scopes", "fields", "type", "payload", "id", "title", "status", "style", "options", "params", "intentUuid", "event", "callback", "sdk", "src_default", "sdk"]
|
|
7
|
+
}
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { FidelityBalance, FidelityHistoryParams, FidelityTransaction, HapticStyle, LocationData, NetworkOptions, NetworkResponse, PaymentRequest, UserProfile } from "@sdk/types";
|
|
2
|
+
export declare class MiniAppSDK {
|
|
3
|
+
private bridge;
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* Local SQLite Storage (Isolated per Mini-App)
|
|
7
|
+
*/
|
|
8
|
+
storage: {
|
|
9
|
+
get: (key: string) => Promise<string | null>;
|
|
10
|
+
getAll: () => Promise<Record<string, string>>;
|
|
11
|
+
set: (key: string, value: string) => Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Hardware-level Secure Storage (Isolated per Mini-App)
|
|
15
|
+
*/
|
|
16
|
+
secureStore: {
|
|
17
|
+
get: (key: string) => Promise<string | null>;
|
|
18
|
+
set: (key: string, value: string) => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Device Location
|
|
22
|
+
*/
|
|
23
|
+
location: {
|
|
24
|
+
getCurrentPosition: () => Promise<LocationData>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Identity & Profile
|
|
28
|
+
*/
|
|
29
|
+
user: {
|
|
30
|
+
/**
|
|
31
|
+
* Fetch a short-lived Scoped Identity Token (JWT) signed by the UP Hub.
|
|
32
|
+
*/
|
|
33
|
+
getIdentityToken: (scopes?: string[]) => Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Fetch user profile data.
|
|
36
|
+
*/
|
|
37
|
+
getProfile: <T extends keyof UserProfile>(fields?: T[]) => Promise<Partial<UserProfile>>;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Explicit Permission Management
|
|
41
|
+
*/
|
|
42
|
+
permission: {
|
|
43
|
+
request: {
|
|
44
|
+
(type: "location"): Promise<boolean>;
|
|
45
|
+
(type: "profile", fields: (keyof UserProfile)[]): Promise<boolean>;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Payments
|
|
50
|
+
*/
|
|
51
|
+
payment: {
|
|
52
|
+
request: (payload: PaymentRequest) => Promise<{
|
|
53
|
+
success: true;
|
|
54
|
+
transactionId: string;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Live Activities & Notifications
|
|
59
|
+
*/
|
|
60
|
+
activity: {
|
|
61
|
+
start: (id: string, title: string, status: string) => Promise<void>;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Haptics
|
|
65
|
+
*/
|
|
66
|
+
haptic: {
|
|
67
|
+
trigger: (style: HapticStyle) => Promise<void>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Network Proxy
|
|
71
|
+
*/
|
|
72
|
+
network: {
|
|
73
|
+
request: (options: NetworkOptions) => Promise<NetworkResponse>;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Fidelity & Rewards
|
|
77
|
+
*/
|
|
78
|
+
fidelity: {
|
|
79
|
+
getFidelityBalance: () => Promise<FidelityBalance>;
|
|
80
|
+
getFidelityHistory: (params?: FidelityHistoryParams) => Promise<FidelityTransaction[]>;
|
|
81
|
+
spendFPoints: (intentUuid: string) => Promise<{
|
|
82
|
+
success: boolean;
|
|
83
|
+
transactionId: string;
|
|
84
|
+
}>;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Navigation & UI
|
|
88
|
+
*/
|
|
89
|
+
ui: {
|
|
90
|
+
exit: () => Promise<void>;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Event Listeners
|
|
94
|
+
*/
|
|
95
|
+
on(event: string, callback: (data: unknown) => void): () => void;
|
|
96
|
+
off(event: string, callback: (data: unknown) => void): void;
|
|
97
|
+
}
|
|
98
|
+
export declare const sdk: MiniAppSDK;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UP Mini-App SDK Types
|
|
3
|
+
*/
|
|
4
|
+
export interface UserProfile {
|
|
5
|
+
uuid: string;
|
|
6
|
+
fullName: string;
|
|
7
|
+
email: string;
|
|
8
|
+
username: string;
|
|
9
|
+
countryCode: string;
|
|
10
|
+
localNumber: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LocationData {
|
|
13
|
+
coords: {
|
|
14
|
+
latitude: number;
|
|
15
|
+
longitude: number;
|
|
16
|
+
altitude: number | null;
|
|
17
|
+
accuracy: number | null;
|
|
18
|
+
altitudeAccuracy: number | null;
|
|
19
|
+
heading: number | null;
|
|
20
|
+
speed: number | null;
|
|
21
|
+
};
|
|
22
|
+
timestamp: number;
|
|
23
|
+
}
|
|
24
|
+
export interface PaymentRequest {
|
|
25
|
+
amount: number;
|
|
26
|
+
currency: string;
|
|
27
|
+
total: number;
|
|
28
|
+
items: Array<{
|
|
29
|
+
item: string;
|
|
30
|
+
price?: number;
|
|
31
|
+
quantity?: number;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export type HapticStyle = "light" | "medium" | "heavy" | "rigid" | "soft" | "success" | "warning" | "error";
|
|
35
|
+
export interface BridgeEnvelope {
|
|
36
|
+
callbackId?: string;
|
|
37
|
+
type: string;
|
|
38
|
+
payload?: unknown;
|
|
39
|
+
success?: boolean;
|
|
40
|
+
error?: string;
|
|
41
|
+
value?: unknown;
|
|
42
|
+
event?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Network Proxy Types
|
|
46
|
+
*/
|
|
47
|
+
export interface NetworkOptions {
|
|
48
|
+
url: string;
|
|
49
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
50
|
+
body?: unknown;
|
|
51
|
+
headers?: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
export interface NetworkResponse {
|
|
54
|
+
status: number;
|
|
55
|
+
data: unknown;
|
|
56
|
+
headers: Record<string, string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fidelity & Rewards Types
|
|
60
|
+
*/
|
|
61
|
+
export interface FidelityBalance {
|
|
62
|
+
balance: number;
|
|
63
|
+
currency: string;
|
|
64
|
+
}
|
|
65
|
+
export interface FidelityHistoryParams {
|
|
66
|
+
limit?: number;
|
|
67
|
+
offset?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface FidelityTransaction {
|
|
70
|
+
id: string;
|
|
71
|
+
amount: number;
|
|
72
|
+
description: string;
|
|
73
|
+
date: string;
|
|
74
|
+
type: "credit" | "debit";
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cloudparse/up-miniapps-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "UP Mini-App SDK for bridge communication between WebView and Host Shell",
|
|
5
|
+
"author": "CloudParse",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"browser": "./dist/index.browser.min.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "node build.js",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"format": "biome format --write .",
|
|
29
|
+
"lint": "biome lint .",
|
|
30
|
+
"lint.fix": "biome check --write .",
|
|
31
|
+
"prepack": "npm run build",
|
|
32
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@biomejs/biome": "^2.4.13",
|
|
36
|
+
"esbuild": "^0.20.0",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|