@az-spaces/aza-miniapp-sdk 1.0.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 +182 -0
- package/dist/index.d.mts +145 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +90 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +84 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# @jumpspaces/aza-miniapp-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for building **Aza Mini Apps**.
|
|
4
|
+
|
|
5
|
+
The Aza native app injects `window.aza` into your WebView automatically — you don't ship any runtime code. This package gives you full TypeScript types and helpers so your IDE autocompletes correctly and your code is type-safe.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
**From npm (public):**
|
|
12
|
+
```bash
|
|
13
|
+
npm install @jumpspaces/aza-miniapp-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**From GitHub Packages (AZ-SPACES org members):**
|
|
17
|
+
```bash
|
|
18
|
+
# Add to your project's .npmrc:
|
|
19
|
+
# @az-spaces:registry=https://npm.pkg.github.com/
|
|
20
|
+
# //npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
|
|
21
|
+
|
|
22
|
+
npm install @az-spaces/aza-miniapp-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { waitForAza } from '@jumpspaces/aza-miniapp-sdk';
|
|
31
|
+
|
|
32
|
+
const aza = await waitForAza();
|
|
33
|
+
const user = await aza.getUser();
|
|
34
|
+
console.log(`Hello, ${user.firstName}!`);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## API reference
|
|
40
|
+
|
|
41
|
+
### `waitForAza(timeoutMs?): Promise<AzaSDK>`
|
|
42
|
+
|
|
43
|
+
Resolves with the bridge once it is ready. Safe to call at any point — if the bridge is already injected it resolves immediately; otherwise it waits for the `azaReady` event (dispatched before your page's first script runs).
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const aza = await waitForAza(); // default 5 s timeout
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `getAza(): AzaSDK`
|
|
50
|
+
|
|
51
|
+
Synchronous version. Throws `AzaNotAvailableError` if the bridge isn't present yet. Use `waitForAza()` unless you are certain the bridge is already available (e.g. inside an event handler that runs after `azaReady`).
|
|
52
|
+
|
|
53
|
+
### `isInsideAza(): boolean`
|
|
54
|
+
|
|
55
|
+
Returns `true` when running inside the Aza app. Use this to gracefully degrade when your app is also served standalone (useful during local development).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
if (isInsideAza()) {
|
|
59
|
+
const aza = getAza();
|
|
60
|
+
// ...
|
|
61
|
+
} else {
|
|
62
|
+
// show a "Open in Aza" banner
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `useAza(timeoutMs?): AzaHookState` *(React only)*
|
|
67
|
+
|
|
68
|
+
React hook version.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useAza } from '@jumpspaces/aza-miniapp-sdk';
|
|
72
|
+
|
|
73
|
+
function App() {
|
|
74
|
+
const { status, aza } = useAza();
|
|
75
|
+
|
|
76
|
+
if (status === 'loading') return <Spinner />;
|
|
77
|
+
if (status === 'unavailable') return <p>Please open this in Aza.</p>;
|
|
78
|
+
|
|
79
|
+
return <button onClick={() => aza.close()}>Close</button>;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## `AzaSDK` methods
|
|
86
|
+
|
|
87
|
+
All methods return a `Promise` that rejects with an `Error` if the operation fails or the required permission was not granted.
|
|
88
|
+
|
|
89
|
+
### `aza.getUser() → Promise<AzaUser>`
|
|
90
|
+
|
|
91
|
+
Returns the authenticated user's profile. Always available once the user has given consent.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const user = await aza.getUser();
|
|
95
|
+
// { username, firstName, lastName, avatarUrl, phone?, email? }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`phone` and `email` are only present if your app declared `USER_PHONE` / `USER_EMAIL` in its permissions and the user approved them.
|
|
99
|
+
|
|
100
|
+
### `aza.getBalance() → Promise<AzaBalance>`
|
|
101
|
+
|
|
102
|
+
Returns the user's current wallet balance in GHS.
|
|
103
|
+
|
|
104
|
+
Requires: `READ_BALANCE` permission.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const { balance } = await aza.getBalance();
|
|
108
|
+
// { balance: 245.50 }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `aza.requestPayment(params) → Promise<AzaPaymentResult>`
|
|
112
|
+
|
|
113
|
+
Shows a **native confirmation dialog** in Aza before any money moves. The Promise resolves only after the user taps "Confirm".
|
|
114
|
+
|
|
115
|
+
Requires: `MAKE_PAYMENTS` permission.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const result = await aza.requestPayment({
|
|
119
|
+
amount: 5.00,
|
|
120
|
+
recipientIdentifier: 'your_aza_username', // your Aza account
|
|
121
|
+
note: 'Premium subscription',
|
|
122
|
+
idempotencyKey: crypto.randomUUID(), // generate a fresh key per attempt
|
|
123
|
+
});
|
|
124
|
+
// { transactionId, status: 'COMPLETED', amount, recipientUsername, note }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Important:** Generate a new `idempotencyKey` for every new payment intent. Reusing the same key on a retry is safe — Aza will return the original result without charging again.
|
|
128
|
+
|
|
129
|
+
### `aza.close() → Promise<void>`
|
|
130
|
+
|
|
131
|
+
Closes the mini app and returns the user to the Aza hub.
|
|
132
|
+
|
|
133
|
+
### `aza.share(options) → Promise<void>`
|
|
134
|
+
|
|
135
|
+
Opens the native Aza share sheet.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
await aza.share({ title: 'Check this out', message: 'I just paid with Aza!' });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Declaring permissions
|
|
144
|
+
|
|
145
|
+
Permissions must be declared when you submit your app via the Aza Developer dashboard. Users see them on a consent sheet on first launch.
|
|
146
|
+
|
|
147
|
+
| Permission key | What it grants |
|
|
148
|
+
|---------------------|---------------------------------------------------|
|
|
149
|
+
| `USER_PROFILE` | name, username, avatar (always required) |
|
|
150
|
+
| `USER_PHONE` | phone number |
|
|
151
|
+
| `USER_EMAIL` | email address |
|
|
152
|
+
| `MAKE_PAYMENTS` | initiate payments from the user's Aza wallet |
|
|
153
|
+
| `READ_BALANCE` | read the user's wallet balance |
|
|
154
|
+
| `READ_TRANSACTIONS` | read recent transaction history *(coming soon)* |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
During local development `window.aza` is not available (no native bridge). Use `isInsideAza()` to detect this and show a fallback, or mock the bridge:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
// dev-mock.ts (never ship this to production)
|
|
164
|
+
if (process.env.NODE_ENV === 'development' && !window.aza) {
|
|
165
|
+
window.aza = {
|
|
166
|
+
version: 'mock',
|
|
167
|
+
getUser: async () => ({ username: 'testuser', firstName: 'Test', lastName: 'User', avatarUrl: null }),
|
|
168
|
+
getBalance: async () => ({ balance: 100.00 }),
|
|
169
|
+
requestPayment: async (p) => ({ transactionId: 'mock-tx', status: 'COMPLETED', amount: p.amount, recipientUsername: p.recipientIdentifier, note: p.note ?? null }),
|
|
170
|
+
close: async () => {},
|
|
171
|
+
share: async () => {},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## App URL requirements
|
|
179
|
+
|
|
180
|
+
- Must use **HTTPS**. HTTP URLs are rejected at submission time.
|
|
181
|
+
- Must be reachable from a mobile WebView (no localhost).
|
|
182
|
+
- Your server must not block WebView user agents.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aza/miniapp-sdk
|
|
3
|
+
*
|
|
4
|
+
* Developer SDK for building Aza Mini Apps.
|
|
5
|
+
* The `window.aza` bridge is injected automatically by the Aza app at runtime —
|
|
6
|
+
* you do NOT need to include any runtime script. This package provides:
|
|
7
|
+
*
|
|
8
|
+
* - TypeScript types for the entire `window.aza` API
|
|
9
|
+
* - `getAza()` — safe accessor that throws if called outside Aza
|
|
10
|
+
* - `waitForAza()` — Promise that resolves once the bridge is ready
|
|
11
|
+
* - `isInsideAza()` — synchronous check
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { waitForAza } from '@aza/miniapp-sdk';
|
|
15
|
+
*
|
|
16
|
+
* const aza = await waitForAza();
|
|
17
|
+
* const user = await aza.getUser();
|
|
18
|
+
*/
|
|
19
|
+
interface AzaUser {
|
|
20
|
+
username: string;
|
|
21
|
+
firstName: string;
|
|
22
|
+
lastName: string;
|
|
23
|
+
avatarUrl: string | null;
|
|
24
|
+
/** Only present if USER_PHONE permission was granted. */
|
|
25
|
+
phone?: string;
|
|
26
|
+
/** Only present if USER_EMAIL permission was granted. */
|
|
27
|
+
email?: string;
|
|
28
|
+
}
|
|
29
|
+
interface AzaBalance {
|
|
30
|
+
balance: number;
|
|
31
|
+
}
|
|
32
|
+
interface AzaPaymentRequest {
|
|
33
|
+
/** Amount in GHS. */
|
|
34
|
+
amount: number;
|
|
35
|
+
/**
|
|
36
|
+
* The Aza username, phone number, or email of the payment recipient.
|
|
37
|
+
* Usually your own Aza account identifier as the mini-app developer.
|
|
38
|
+
*/
|
|
39
|
+
recipientIdentifier: string;
|
|
40
|
+
/** Short description shown on the user's receipt. Max 200 chars. */
|
|
41
|
+
note?: string;
|
|
42
|
+
/**
|
|
43
|
+
* A unique key you generate per payment attempt.
|
|
44
|
+
* Aza will deduplicate based on this — safe to retry on network error.
|
|
45
|
+
*/
|
|
46
|
+
idempotencyKey: string;
|
|
47
|
+
}
|
|
48
|
+
interface AzaPaymentResult {
|
|
49
|
+
transactionId: string;
|
|
50
|
+
status: 'PENDING' | 'COMPLETED' | 'FAILED';
|
|
51
|
+
amount: number;
|
|
52
|
+
recipientUsername: string;
|
|
53
|
+
note: string | null;
|
|
54
|
+
}
|
|
55
|
+
interface AzaShareOptions {
|
|
56
|
+
title?: string;
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
interface AzaSDK {
|
|
60
|
+
/**
|
|
61
|
+
* Get the current user's profile.
|
|
62
|
+
* Always available after consent — no extra permission needed beyond USER_PROFILE.
|
|
63
|
+
*/
|
|
64
|
+
getUser(): Promise<AzaUser>;
|
|
65
|
+
/**
|
|
66
|
+
* Get the user's current Aza wallet balance in GHS.
|
|
67
|
+
* Requires READ_BALANCE permission to be declared and consented.
|
|
68
|
+
*/
|
|
69
|
+
getBalance(): Promise<AzaBalance>;
|
|
70
|
+
/**
|
|
71
|
+
* Request a payment from the user.
|
|
72
|
+
*
|
|
73
|
+
* The Aza app shows a native confirmation dialog — the user must tap "Confirm"
|
|
74
|
+
* before any money moves. Your Promise resolves only after confirmation.
|
|
75
|
+
*
|
|
76
|
+
* Requires MAKE_PAYMENTS permission to be declared and consented.
|
|
77
|
+
*/
|
|
78
|
+
requestPayment(params: AzaPaymentRequest): Promise<AzaPaymentResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Close this mini app and return the user to the Aza hub.
|
|
81
|
+
*/
|
|
82
|
+
close(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Open the native Aza share sheet.
|
|
85
|
+
*/
|
|
86
|
+
share(options: AzaShareOptions): Promise<void>;
|
|
87
|
+
/** SDK version string, e.g. "1.0.0" */
|
|
88
|
+
readonly version: string;
|
|
89
|
+
}
|
|
90
|
+
declare global {
|
|
91
|
+
interface Window {
|
|
92
|
+
/** Injected by the Aza native app before page content loads. */
|
|
93
|
+
aza?: AzaSDK;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns `true` when running inside the Aza app with the bridge available.
|
|
98
|
+
*/
|
|
99
|
+
declare function isInsideAza(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Synchronously returns the `window.aza` bridge.
|
|
102
|
+
* Throws `AzaNotAvailableError` if called outside the Aza app or before the bridge is ready.
|
|
103
|
+
*/
|
|
104
|
+
declare function getAza(): AzaSDK;
|
|
105
|
+
/**
|
|
106
|
+
* Waits for the Aza bridge to be injected and returns it.
|
|
107
|
+
*
|
|
108
|
+
* - If `window.aza` is already available, resolves immediately.
|
|
109
|
+
* - If the page loads before the bridge (e.g. in dev), waits for the `azaReady` event.
|
|
110
|
+
* - Rejects after `timeoutMs` (default 5 000 ms) if the bridge never arrives.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const aza = await waitForAza();
|
|
114
|
+
* const user = await aza.getUser();
|
|
115
|
+
*/
|
|
116
|
+
declare function waitForAza(timeoutMs?: number): Promise<AzaSDK>;
|
|
117
|
+
/**
|
|
118
|
+
* Thrown when the Aza SDK is not available in the current environment.
|
|
119
|
+
*/
|
|
120
|
+
declare class AzaNotAvailableError extends Error {
|
|
121
|
+
constructor(message: string);
|
|
122
|
+
}
|
|
123
|
+
type AzaHookState = {
|
|
124
|
+
status: 'loading';
|
|
125
|
+
} | {
|
|
126
|
+
status: 'ready';
|
|
127
|
+
aza: AzaSDK;
|
|
128
|
+
} | {
|
|
129
|
+
status: 'unavailable';
|
|
130
|
+
error: AzaNotAvailableError;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* React hook that resolves the Aza bridge.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* function MyApp() {
|
|
137
|
+
* const { status, aza } = useAza();
|
|
138
|
+
* if (status === 'loading') return <Spinner />;
|
|
139
|
+
* if (status === 'unavailable') return <p>Open this in Aza</p>;
|
|
140
|
+
* // aza is fully typed as AzaSDK
|
|
141
|
+
* }
|
|
142
|
+
*/
|
|
143
|
+
declare function useAza(timeoutMs?: number): AzaHookState;
|
|
144
|
+
|
|
145
|
+
export { type AzaBalance, type AzaHookState, AzaNotAvailableError, type AzaPaymentRequest, type AzaPaymentResult, type AzaSDK, type AzaShareOptions, type AzaUser, getAza, isInsideAza, useAza, waitForAza };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @aza/miniapp-sdk
|
|
3
|
+
*
|
|
4
|
+
* Developer SDK for building Aza Mini Apps.
|
|
5
|
+
* The `window.aza` bridge is injected automatically by the Aza app at runtime —
|
|
6
|
+
* you do NOT need to include any runtime script. This package provides:
|
|
7
|
+
*
|
|
8
|
+
* - TypeScript types for the entire `window.aza` API
|
|
9
|
+
* - `getAza()` — safe accessor that throws if called outside Aza
|
|
10
|
+
* - `waitForAza()` — Promise that resolves once the bridge is ready
|
|
11
|
+
* - `isInsideAza()` — synchronous check
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { waitForAza } from '@aza/miniapp-sdk';
|
|
15
|
+
*
|
|
16
|
+
* const aza = await waitForAza();
|
|
17
|
+
* const user = await aza.getUser();
|
|
18
|
+
*/
|
|
19
|
+
interface AzaUser {
|
|
20
|
+
username: string;
|
|
21
|
+
firstName: string;
|
|
22
|
+
lastName: string;
|
|
23
|
+
avatarUrl: string | null;
|
|
24
|
+
/** Only present if USER_PHONE permission was granted. */
|
|
25
|
+
phone?: string;
|
|
26
|
+
/** Only present if USER_EMAIL permission was granted. */
|
|
27
|
+
email?: string;
|
|
28
|
+
}
|
|
29
|
+
interface AzaBalance {
|
|
30
|
+
balance: number;
|
|
31
|
+
}
|
|
32
|
+
interface AzaPaymentRequest {
|
|
33
|
+
/** Amount in GHS. */
|
|
34
|
+
amount: number;
|
|
35
|
+
/**
|
|
36
|
+
* The Aza username, phone number, or email of the payment recipient.
|
|
37
|
+
* Usually your own Aza account identifier as the mini-app developer.
|
|
38
|
+
*/
|
|
39
|
+
recipientIdentifier: string;
|
|
40
|
+
/** Short description shown on the user's receipt. Max 200 chars. */
|
|
41
|
+
note?: string;
|
|
42
|
+
/**
|
|
43
|
+
* A unique key you generate per payment attempt.
|
|
44
|
+
* Aza will deduplicate based on this — safe to retry on network error.
|
|
45
|
+
*/
|
|
46
|
+
idempotencyKey: string;
|
|
47
|
+
}
|
|
48
|
+
interface AzaPaymentResult {
|
|
49
|
+
transactionId: string;
|
|
50
|
+
status: 'PENDING' | 'COMPLETED' | 'FAILED';
|
|
51
|
+
amount: number;
|
|
52
|
+
recipientUsername: string;
|
|
53
|
+
note: string | null;
|
|
54
|
+
}
|
|
55
|
+
interface AzaShareOptions {
|
|
56
|
+
title?: string;
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
interface AzaSDK {
|
|
60
|
+
/**
|
|
61
|
+
* Get the current user's profile.
|
|
62
|
+
* Always available after consent — no extra permission needed beyond USER_PROFILE.
|
|
63
|
+
*/
|
|
64
|
+
getUser(): Promise<AzaUser>;
|
|
65
|
+
/**
|
|
66
|
+
* Get the user's current Aza wallet balance in GHS.
|
|
67
|
+
* Requires READ_BALANCE permission to be declared and consented.
|
|
68
|
+
*/
|
|
69
|
+
getBalance(): Promise<AzaBalance>;
|
|
70
|
+
/**
|
|
71
|
+
* Request a payment from the user.
|
|
72
|
+
*
|
|
73
|
+
* The Aza app shows a native confirmation dialog — the user must tap "Confirm"
|
|
74
|
+
* before any money moves. Your Promise resolves only after confirmation.
|
|
75
|
+
*
|
|
76
|
+
* Requires MAKE_PAYMENTS permission to be declared and consented.
|
|
77
|
+
*/
|
|
78
|
+
requestPayment(params: AzaPaymentRequest): Promise<AzaPaymentResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Close this mini app and return the user to the Aza hub.
|
|
81
|
+
*/
|
|
82
|
+
close(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Open the native Aza share sheet.
|
|
85
|
+
*/
|
|
86
|
+
share(options: AzaShareOptions): Promise<void>;
|
|
87
|
+
/** SDK version string, e.g. "1.0.0" */
|
|
88
|
+
readonly version: string;
|
|
89
|
+
}
|
|
90
|
+
declare global {
|
|
91
|
+
interface Window {
|
|
92
|
+
/** Injected by the Aza native app before page content loads. */
|
|
93
|
+
aza?: AzaSDK;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns `true` when running inside the Aza app with the bridge available.
|
|
98
|
+
*/
|
|
99
|
+
declare function isInsideAza(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Synchronously returns the `window.aza` bridge.
|
|
102
|
+
* Throws `AzaNotAvailableError` if called outside the Aza app or before the bridge is ready.
|
|
103
|
+
*/
|
|
104
|
+
declare function getAza(): AzaSDK;
|
|
105
|
+
/**
|
|
106
|
+
* Waits for the Aza bridge to be injected and returns it.
|
|
107
|
+
*
|
|
108
|
+
* - If `window.aza` is already available, resolves immediately.
|
|
109
|
+
* - If the page loads before the bridge (e.g. in dev), waits for the `azaReady` event.
|
|
110
|
+
* - Rejects after `timeoutMs` (default 5 000 ms) if the bridge never arrives.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const aza = await waitForAza();
|
|
114
|
+
* const user = await aza.getUser();
|
|
115
|
+
*/
|
|
116
|
+
declare function waitForAza(timeoutMs?: number): Promise<AzaSDK>;
|
|
117
|
+
/**
|
|
118
|
+
* Thrown when the Aza SDK is not available in the current environment.
|
|
119
|
+
*/
|
|
120
|
+
declare class AzaNotAvailableError extends Error {
|
|
121
|
+
constructor(message: string);
|
|
122
|
+
}
|
|
123
|
+
type AzaHookState = {
|
|
124
|
+
status: 'loading';
|
|
125
|
+
} | {
|
|
126
|
+
status: 'ready';
|
|
127
|
+
aza: AzaSDK;
|
|
128
|
+
} | {
|
|
129
|
+
status: 'unavailable';
|
|
130
|
+
error: AzaNotAvailableError;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* React hook that resolves the Aza bridge.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* function MyApp() {
|
|
137
|
+
* const { status, aza } = useAza();
|
|
138
|
+
* if (status === 'loading') return <Spinner />;
|
|
139
|
+
* if (status === 'unavailable') return <p>Open this in Aza</p>;
|
|
140
|
+
* // aza is fully typed as AzaSDK
|
|
141
|
+
* }
|
|
142
|
+
*/
|
|
143
|
+
declare function useAza(timeoutMs?: number): AzaHookState;
|
|
144
|
+
|
|
145
|
+
export { type AzaBalance, type AzaHookState, AzaNotAvailableError, type AzaPaymentRequest, type AzaPaymentResult, type AzaSDK, type AzaShareOptions, type AzaUser, getAza, isInsideAza, useAza, waitForAza };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// src/index.ts
|
|
11
|
+
function isInsideAza() {
|
|
12
|
+
return typeof window !== "undefined" && typeof window.aza !== "undefined";
|
|
13
|
+
}
|
|
14
|
+
function getAza() {
|
|
15
|
+
if (!isInsideAza()) {
|
|
16
|
+
throw new AzaNotAvailableError(
|
|
17
|
+
"window.aza is not available. Make sure your app is running inside the Aza mini app player."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return window.aza;
|
|
21
|
+
}
|
|
22
|
+
function waitForAza(timeoutMs = 5e3) {
|
|
23
|
+
if (isInsideAza()) {
|
|
24
|
+
return Promise.resolve(window.aza);
|
|
25
|
+
}
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
window.removeEventListener("azaReady", onReady);
|
|
29
|
+
reject(new AzaNotAvailableError(
|
|
30
|
+
`Aza bridge did not initialise within ${timeoutMs}ms. Make sure your app is running inside the Aza mini app player.`
|
|
31
|
+
));
|
|
32
|
+
}, timeoutMs);
|
|
33
|
+
function onReady() {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
window.removeEventListener("azaReady", onReady);
|
|
36
|
+
if (window.aza) {
|
|
37
|
+
resolve(window.aza);
|
|
38
|
+
} else {
|
|
39
|
+
reject(new AzaNotAvailableError("azaReady fired but window.aza is still undefined."));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
window.addEventListener("azaReady", onReady);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
var AzaNotAvailableError = class extends Error {
|
|
46
|
+
constructor(message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "AzaNotAvailableError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function useAza(timeoutMs = 5e3) {
|
|
52
|
+
const React = _requireReact();
|
|
53
|
+
if (!React) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
"useAza() requires React. Install react and react-dom, or use waitForAza() instead."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const [state, setState] = React.useState(
|
|
59
|
+
isInsideAza() ? { status: "ready", aza: window.aza } : { status: "loading" }
|
|
60
|
+
);
|
|
61
|
+
React.useEffect(() => {
|
|
62
|
+
if (state.status === "ready") return;
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
waitForAza(timeoutMs).then((aza) => {
|
|
65
|
+
if (!cancelled) setState({ status: "ready", aza });
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
if (!cancelled) setState({ status: "unavailable", error });
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
cancelled = true;
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
73
|
+
return state;
|
|
74
|
+
}
|
|
75
|
+
function _requireReact() {
|
|
76
|
+
try {
|
|
77
|
+
if (typeof __require === "undefined") return null;
|
|
78
|
+
return __require("react");
|
|
79
|
+
} catch (e) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.AzaNotAvailableError = AzaNotAvailableError;
|
|
85
|
+
exports.getAza = getAza;
|
|
86
|
+
exports.isInsideAza = isInsideAza;
|
|
87
|
+
exports.useAza = useAza;
|
|
88
|
+
exports.waitForAza = waitForAza;
|
|
89
|
+
//# sourceMappingURL=index.js.map
|
|
90
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @aza/miniapp-sdk\n *\n * Developer SDK for building Aza Mini Apps.\n * The `window.aza` bridge is injected automatically by the Aza app at runtime —\n * you do NOT need to include any runtime script. This package provides:\n *\n * - TypeScript types for the entire `window.aza` API\n * - `getAza()` — safe accessor that throws if called outside Aza\n * - `waitForAza()` — Promise that resolves once the bridge is ready\n * - `isInsideAza()` — synchronous check\n *\n * Usage:\n * import { waitForAza } from '@aza/miniapp-sdk';\n *\n * const aza = await waitForAza();\n * const user = await aza.getUser();\n */\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AzaUser {\n username: string;\n firstName: string;\n lastName: string;\n avatarUrl: string | null;\n /** Only present if USER_PHONE permission was granted. */\n phone?: string;\n /** Only present if USER_EMAIL permission was granted. */\n email?: string;\n}\n\nexport interface AzaBalance {\n balance: number;\n}\n\nexport interface AzaPaymentRequest {\n /** Amount in GHS. */\n amount: number;\n /**\n * The Aza username, phone number, or email of the payment recipient.\n * Usually your own Aza account identifier as the mini-app developer.\n */\n recipientIdentifier: string;\n /** Short description shown on the user's receipt. Max 200 chars. */\n note?: string;\n /**\n * A unique key you generate per payment attempt.\n * Aza will deduplicate based on this — safe to retry on network error.\n */\n idempotencyKey: string;\n}\n\nexport interface AzaPaymentResult {\n transactionId: string;\n status: 'PENDING' | 'COMPLETED' | 'FAILED';\n amount: number;\n recipientUsername: string;\n note: string | null;\n}\n\nexport interface AzaShareOptions {\n title?: string;\n message: string;\n}\n\nexport interface AzaSDK {\n /**\n * Get the current user's profile.\n * Always available after consent — no extra permission needed beyond USER_PROFILE.\n */\n getUser(): Promise<AzaUser>;\n\n /**\n * Get the user's current Aza wallet balance in GHS.\n * Requires READ_BALANCE permission to be declared and consented.\n */\n getBalance(): Promise<AzaBalance>;\n\n /**\n * Request a payment from the user.\n *\n * The Aza app shows a native confirmation dialog — the user must tap \"Confirm\"\n * before any money moves. Your Promise resolves only after confirmation.\n *\n * Requires MAKE_PAYMENTS permission to be declared and consented.\n */\n requestPayment(params: AzaPaymentRequest): Promise<AzaPaymentResult>;\n\n /**\n * Close this mini app and return the user to the Aza hub.\n */\n close(): Promise<void>;\n\n /**\n * Open the native Aza share sheet.\n */\n share(options: AzaShareOptions): Promise<void>;\n\n /** SDK version string, e.g. \"1.0.0\" */\n readonly version: string;\n}\n\n// ─── Global augmentation ──────────────────────────────────────────────────────\n\ndeclare global {\n interface Window {\n /** Injected by the Aza native app before page content loads. */\n aza?: AzaSDK;\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when running inside the Aza app with the bridge available.\n */\nexport function isInsideAza(): boolean {\n return typeof window !== 'undefined' && typeof window.aza !== 'undefined';\n}\n\n/**\n * Synchronously returns the `window.aza` bridge.\n * Throws `AzaNotAvailableError` if called outside the Aza app or before the bridge is ready.\n */\nexport function getAza(): AzaSDK {\n if (!isInsideAza()) {\n throw new AzaNotAvailableError(\n 'window.aza is not available. Make sure your app is running inside the Aza mini app player.'\n );\n }\n return window.aza!;\n}\n\n/**\n * Waits for the Aza bridge to be injected and returns it.\n *\n * - If `window.aza` is already available, resolves immediately.\n * - If the page loads before the bridge (e.g. in dev), waits for the `azaReady` event.\n * - Rejects after `timeoutMs` (default 5 000 ms) if the bridge never arrives.\n *\n * @example\n * const aza = await waitForAza();\n * const user = await aza.getUser();\n */\nexport function waitForAza(timeoutMs = 5_000): Promise<AzaSDK> {\n if (isInsideAza()) {\n return Promise.resolve(window.aza!);\n }\n\n return new Promise<AzaSDK>((resolve, reject) => {\n const timer = setTimeout(() => {\n window.removeEventListener('azaReady', onReady);\n reject(new AzaNotAvailableError(\n `Aza bridge did not initialise within ${timeoutMs}ms. ` +\n 'Make sure your app is running inside the Aza mini app player.'\n ));\n }, timeoutMs);\n\n function onReady() {\n clearTimeout(timer);\n window.removeEventListener('azaReady', onReady);\n if (window.aza) {\n resolve(window.aza);\n } else {\n reject(new AzaNotAvailableError('azaReady fired but window.aza is still undefined.'));\n }\n }\n\n window.addEventListener('azaReady', onReady);\n });\n}\n\n/**\n * Thrown when the Aza SDK is not available in the current environment.\n */\nexport class AzaNotAvailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AzaNotAvailableError';\n }\n}\n\n// ─── React hook (optional, tree-shakeable) ────────────────────────────────────\n\nexport type AzaHookState =\n | { status: 'loading' }\n | { status: 'ready'; aza: AzaSDK }\n | { status: 'unavailable'; error: AzaNotAvailableError };\n\n/**\n * React hook that resolves the Aza bridge.\n *\n * @example\n * function MyApp() {\n * const { status, aza } = useAza();\n * if (status === 'loading') return <Spinner />;\n * if (status === 'unavailable') return <p>Open this in Aza</p>;\n * // aza is fully typed as AzaSDK\n * }\n */\nexport function useAza(timeoutMs = 5_000): AzaHookState {\n // Lazy require React so the rest of the SDK works without React installed\n // (plain HTML / Vue / Svelte apps don't need React)\n const React = _requireReact();\n if (!React) {\n throw new Error(\n 'useAza() requires React. Install react and react-dom, or use waitForAza() instead.'\n );\n }\n\n const [state, setState] = React.useState<AzaHookState>(\n isInsideAza() ? { status: 'ready', aza: window.aza! } : { status: 'loading' }\n );\n\n React.useEffect(() => {\n if (state.status === 'ready') return;\n let cancelled = false;\n\n waitForAza(timeoutMs)\n .then((aza) => { if (!cancelled) setState({ status: 'ready', aza }); })\n .catch((error) => { if (!cancelled) setState({ status: 'unavailable', error }); });\n\n return () => { cancelled = true; };\n }, []);\n\n return state;\n}\n\n// `require` is resolved by bundlers (webpack/vite/esbuild) at build time.\n// Declaring it here avoids a dependency on @types/node while staying type-safe.\ndeclare const require: undefined | ((id: string) => unknown);\n\nfunction _requireReact(): typeof import('react') | null {\n try {\n if (typeof require === 'undefined') return null;\n return require('react') as typeof import('react');\n } catch {\n return null;\n }\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
function isInsideAza() {
|
|
10
|
+
return typeof window !== "undefined" && typeof window.aza !== "undefined";
|
|
11
|
+
}
|
|
12
|
+
function getAza() {
|
|
13
|
+
if (!isInsideAza()) {
|
|
14
|
+
throw new AzaNotAvailableError(
|
|
15
|
+
"window.aza is not available. Make sure your app is running inside the Aza mini app player."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return window.aza;
|
|
19
|
+
}
|
|
20
|
+
function waitForAza(timeoutMs = 5e3) {
|
|
21
|
+
if (isInsideAza()) {
|
|
22
|
+
return Promise.resolve(window.aza);
|
|
23
|
+
}
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
window.removeEventListener("azaReady", onReady);
|
|
27
|
+
reject(new AzaNotAvailableError(
|
|
28
|
+
`Aza bridge did not initialise within ${timeoutMs}ms. Make sure your app is running inside the Aza mini app player.`
|
|
29
|
+
));
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
function onReady() {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
window.removeEventListener("azaReady", onReady);
|
|
34
|
+
if (window.aza) {
|
|
35
|
+
resolve(window.aza);
|
|
36
|
+
} else {
|
|
37
|
+
reject(new AzaNotAvailableError("azaReady fired but window.aza is still undefined."));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
window.addEventListener("azaReady", onReady);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
var AzaNotAvailableError = class extends Error {
|
|
44
|
+
constructor(message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = "AzaNotAvailableError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function useAza(timeoutMs = 5e3) {
|
|
50
|
+
const React = _requireReact();
|
|
51
|
+
if (!React) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
"useAza() requires React. Install react and react-dom, or use waitForAza() instead."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const [state, setState] = React.useState(
|
|
57
|
+
isInsideAza() ? { status: "ready", aza: window.aza } : { status: "loading" }
|
|
58
|
+
);
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
if (state.status === "ready") return;
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
waitForAza(timeoutMs).then((aza) => {
|
|
63
|
+
if (!cancelled) setState({ status: "ready", aza });
|
|
64
|
+
}).catch((error) => {
|
|
65
|
+
if (!cancelled) setState({ status: "unavailable", error });
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
cancelled = true;
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
function _requireReact() {
|
|
74
|
+
try {
|
|
75
|
+
if (typeof __require === "undefined") return null;
|
|
76
|
+
return __require("react");
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { AzaNotAvailableError, getAza, isInsideAza, useAza, waitForAza };
|
|
83
|
+
//# sourceMappingURL=index.mjs.map
|
|
84
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @aza/miniapp-sdk\n *\n * Developer SDK for building Aza Mini Apps.\n * The `window.aza` bridge is injected automatically by the Aza app at runtime —\n * you do NOT need to include any runtime script. This package provides:\n *\n * - TypeScript types for the entire `window.aza` API\n * - `getAza()` — safe accessor that throws if called outside Aza\n * - `waitForAza()` — Promise that resolves once the bridge is ready\n * - `isInsideAza()` — synchronous check\n *\n * Usage:\n * import { waitForAza } from '@aza/miniapp-sdk';\n *\n * const aza = await waitForAza();\n * const user = await aza.getUser();\n */\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface AzaUser {\n username: string;\n firstName: string;\n lastName: string;\n avatarUrl: string | null;\n /** Only present if USER_PHONE permission was granted. */\n phone?: string;\n /** Only present if USER_EMAIL permission was granted. */\n email?: string;\n}\n\nexport interface AzaBalance {\n balance: number;\n}\n\nexport interface AzaPaymentRequest {\n /** Amount in GHS. */\n amount: number;\n /**\n * The Aza username, phone number, or email of the payment recipient.\n * Usually your own Aza account identifier as the mini-app developer.\n */\n recipientIdentifier: string;\n /** Short description shown on the user's receipt. Max 200 chars. */\n note?: string;\n /**\n * A unique key you generate per payment attempt.\n * Aza will deduplicate based on this — safe to retry on network error.\n */\n idempotencyKey: string;\n}\n\nexport interface AzaPaymentResult {\n transactionId: string;\n status: 'PENDING' | 'COMPLETED' | 'FAILED';\n amount: number;\n recipientUsername: string;\n note: string | null;\n}\n\nexport interface AzaShareOptions {\n title?: string;\n message: string;\n}\n\nexport interface AzaSDK {\n /**\n * Get the current user's profile.\n * Always available after consent — no extra permission needed beyond USER_PROFILE.\n */\n getUser(): Promise<AzaUser>;\n\n /**\n * Get the user's current Aza wallet balance in GHS.\n * Requires READ_BALANCE permission to be declared and consented.\n */\n getBalance(): Promise<AzaBalance>;\n\n /**\n * Request a payment from the user.\n *\n * The Aza app shows a native confirmation dialog — the user must tap \"Confirm\"\n * before any money moves. Your Promise resolves only after confirmation.\n *\n * Requires MAKE_PAYMENTS permission to be declared and consented.\n */\n requestPayment(params: AzaPaymentRequest): Promise<AzaPaymentResult>;\n\n /**\n * Close this mini app and return the user to the Aza hub.\n */\n close(): Promise<void>;\n\n /**\n * Open the native Aza share sheet.\n */\n share(options: AzaShareOptions): Promise<void>;\n\n /** SDK version string, e.g. \"1.0.0\" */\n readonly version: string;\n}\n\n// ─── Global augmentation ──────────────────────────────────────────────────────\n\ndeclare global {\n interface Window {\n /** Injected by the Aza native app before page content loads. */\n aza?: AzaSDK;\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when running inside the Aza app with the bridge available.\n */\nexport function isInsideAza(): boolean {\n return typeof window !== 'undefined' && typeof window.aza !== 'undefined';\n}\n\n/**\n * Synchronously returns the `window.aza` bridge.\n * Throws `AzaNotAvailableError` if called outside the Aza app or before the bridge is ready.\n */\nexport function getAza(): AzaSDK {\n if (!isInsideAza()) {\n throw new AzaNotAvailableError(\n 'window.aza is not available. Make sure your app is running inside the Aza mini app player.'\n );\n }\n return window.aza!;\n}\n\n/**\n * Waits for the Aza bridge to be injected and returns it.\n *\n * - If `window.aza` is already available, resolves immediately.\n * - If the page loads before the bridge (e.g. in dev), waits for the `azaReady` event.\n * - Rejects after `timeoutMs` (default 5 000 ms) if the bridge never arrives.\n *\n * @example\n * const aza = await waitForAza();\n * const user = await aza.getUser();\n */\nexport function waitForAza(timeoutMs = 5_000): Promise<AzaSDK> {\n if (isInsideAza()) {\n return Promise.resolve(window.aza!);\n }\n\n return new Promise<AzaSDK>((resolve, reject) => {\n const timer = setTimeout(() => {\n window.removeEventListener('azaReady', onReady);\n reject(new AzaNotAvailableError(\n `Aza bridge did not initialise within ${timeoutMs}ms. ` +\n 'Make sure your app is running inside the Aza mini app player.'\n ));\n }, timeoutMs);\n\n function onReady() {\n clearTimeout(timer);\n window.removeEventListener('azaReady', onReady);\n if (window.aza) {\n resolve(window.aza);\n } else {\n reject(new AzaNotAvailableError('azaReady fired but window.aza is still undefined.'));\n }\n }\n\n window.addEventListener('azaReady', onReady);\n });\n}\n\n/**\n * Thrown when the Aza SDK is not available in the current environment.\n */\nexport class AzaNotAvailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AzaNotAvailableError';\n }\n}\n\n// ─── React hook (optional, tree-shakeable) ────────────────────────────────────\n\nexport type AzaHookState =\n | { status: 'loading' }\n | { status: 'ready'; aza: AzaSDK }\n | { status: 'unavailable'; error: AzaNotAvailableError };\n\n/**\n * React hook that resolves the Aza bridge.\n *\n * @example\n * function MyApp() {\n * const { status, aza } = useAza();\n * if (status === 'loading') return <Spinner />;\n * if (status === 'unavailable') return <p>Open this in Aza</p>;\n * // aza is fully typed as AzaSDK\n * }\n */\nexport function useAza(timeoutMs = 5_000): AzaHookState {\n // Lazy require React so the rest of the SDK works without React installed\n // (plain HTML / Vue / Svelte apps don't need React)\n const React = _requireReact();\n if (!React) {\n throw new Error(\n 'useAza() requires React. Install react and react-dom, or use waitForAza() instead.'\n );\n }\n\n const [state, setState] = React.useState<AzaHookState>(\n isInsideAza() ? { status: 'ready', aza: window.aza! } : { status: 'loading' }\n );\n\n React.useEffect(() => {\n if (state.status === 'ready') return;\n let cancelled = false;\n\n waitForAza(timeoutMs)\n .then((aza) => { if (!cancelled) setState({ status: 'ready', aza }); })\n .catch((error) => { if (!cancelled) setState({ status: 'unavailable', error }); });\n\n return () => { cancelled = true; };\n }, []);\n\n return state;\n}\n\n// `require` is resolved by bundlers (webpack/vite/esbuild) at build time.\n// Declaring it here avoids a dependency on @types/node while staying type-safe.\ndeclare const require: undefined | ((id: string) => unknown);\n\nfunction _requireReact(): typeof import('react') | null {\n try {\n if (typeof require === 'undefined') return null;\n return require('react') as typeof import('react');\n } catch {\n return null;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@az-spaces/aza-miniapp-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "SDK for building Aza Mini Apps. Provides TypeScript types and bridge helpers for window.aza.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"publish:npm": "npm publish --access public --registry https://registry.npmjs.org/",
|
|
21
|
+
"publish:github": "npm publish --access public --registry https://npm.pkg.github.com/"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"registry": "https://npm.pkg.github.com/"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/AZ-SPACES/project404.git"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": ">=17"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"react": { "optional": true }
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.4.0",
|
|
39
|
+
"@types/react": "^18.0.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": ["aza", "miniapp", "sdk", "fintech", "ghana"],
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|