@retion-miniapp/sdk 0.1.0 → 0.3.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/CHANGELOG.md +18 -4
- package/README.md +149 -84
- package/dist/index.cjs +168 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -50
- package/dist/index.d.ts +58 -50
- package/dist/index.js +162 -67
- package/dist/index.js.map +1 -1
- package/package.json +4 -24
- package/dist/chunk-C2AZ33LI.cjs +0 -175
- package/dist/chunk-C2AZ33LI.cjs.map +0 -1
- package/dist/chunk-PVXYU6XK.js +0 -165
- package/dist/chunk-PVXYU6XK.js.map +0 -1
- package/dist/types-8g8OpZsj.d.cts +0 -65
- package/dist/types-8g8OpZsj.d.ts +0 -65
- package/dist/vue/index.cjs +0 -19
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.cts +0 -13
- package/dist/vue/index.d.ts +0 -13
- package/dist/vue/index.js +0 -17
- package/dist/vue/index.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.3.0 — 2026-09-30
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `getToken('access' | 'partner' | 'chatbox')` — lấy `partner_token` / `chatbox_token` khi admin đã bật quyền cho mini app trên Console.
|
|
11
|
+
- `ErrorCode.PERMISSION_DENIED`.
|
|
12
|
+
|
|
13
|
+
## 0.2.0 — 2026-09-30
|
|
14
|
+
|
|
15
|
+
### Breaking
|
|
16
|
+
- Thu gọn API về đúng nhu cầu mini app: `getRouteParams`, `getAccessToken`, `closeApp`, `isInHost`.
|
|
17
|
+
- Bỏ `onContextChange`, `getPageInfo`, `getUserInfo`, `sendMessage`, `insertText`, `openWebview`, `showToast`, `getSystemInfo`, `setMockHandler`, callback `{ success, fail }` và entry `@retion-miniapp/sdk/vue`.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- `getAccessToken()` ngoài app đọc `?access_token=` trên URL (thay cho mock handler) — web cũ / iframe dùng được ngay.
|
|
21
|
+
|
|
22
|
+
## 0.1.0 — 2026-09-30
|
|
23
|
+
|
|
7
24
|
### Added
|
|
8
|
-
-
|
|
9
|
-
- API: `getRouteParams`, `onContextChange`, `getPageInfo`, `getAccessToken`, `getUserInfo`, `sendMessage`, `insertText`, `closeApp`, `openWebview`, `showToast`, `getSystemInfo`, `isInHost`, `setMockHandler`.
|
|
10
|
-
- `MiniAppError` + `ErrorCode`.
|
|
11
|
-
- Vue composable `useRouteParams` (`@retion-miniapp/sdk/vue`).
|
|
25
|
+
- Bản đầu: bridge giao thức v1, các API cơ bản, `MiniAppError` + `ErrorCode`.
|
package/README.md
CHANGED
|
@@ -1,134 +1,199 @@
|
|
|
1
1
|
# @retion-miniapp/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Kết nối web mini app (Vue, React, ...) với app Retion.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Mọi API trả `Promise`, hoặc dùng callback `{ success, fail }`.
|
|
7
|
-
- TypeScript, ESM + CJS, tree-shake theo từng hàm, có composable Vue.
|
|
5
|
+
Mini app là web của bạn, được app tải về máy và mở full màn hình trong 1 conversation. SDK làm 3 việc:
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
1. Cho biết mini app đang mở ở **page nào, khách nào** → `getRouteParams()`
|
|
8
|
+
2. Đưa **token** để gọi API backend của bạn → `getAccessToken()`
|
|
9
|
+
3. **Đóng** mini app → `closeApp()`
|
|
10
|
+
|
|
11
|
+
Cùng 1 source chạy được **cả trong app lẫn trên web** (trình duyệt / iframe cũ) — ngoài app, SDK đọc thông tin từ query URL như hiện tại.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Cài đặt
|
|
10
16
|
|
|
11
17
|
```bash
|
|
12
18
|
npm i @retion-miniapp/sdk
|
|
13
|
-
npm i -D @retion-miniapp/cli
|
|
19
|
+
npm i -D @retion-miniapp/cli # build + deploy lên Retion
|
|
20
|
+
npx retion-miniapp init --app-id <app_id> --name "<Tên mini app>"
|
|
14
21
|
```
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
`app_id` lấy trên [Retion Mini App Console](https://retion-miniapp-platform.vercel.app/console) (trùng `app_id` của widget, vd `66515701a2be1c03411c5723`).
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
import { getRouteParams, getAccessToken, insertText, closeApp, isInHost } from '@retion-miniapp/sdk';
|
|
25
|
+
## 2. Cấu hình project
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
Mini app chạy từ file trên máy (không phải từ domain của bạn), nên cần 3 chỉnh nhỏ:
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
const order = await fetch(`https://api.your-app.com/orders?client=${client_id}`, {
|
|
25
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
26
|
-
}).then((r) => r.json());
|
|
29
|
+
**a. Đường dẫn asset tương đối** — `vite.config.ts`:
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
```ts
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
base: './',
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**b. Router dạng hash** (nếu dùng vue-router):
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
createRouter({ history: createWebHashHistory(), routes });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**c. API gọi bằng URL tuyệt đối** + backend cho phép CORS origin `null`:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const API_URL = import.meta.env.VITE_API_URL; // https://api.merchant.vn — không dùng "/api"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 3. Bọc SDK vào 1 file
|
|
50
|
+
|
|
51
|
+
Không gọi SDK rải rác trong component. Tạo 1 file làm cầu nối duy nhất — sau này đổi gì chỉ sửa 1 chỗ:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// src/lib/miniapp.ts
|
|
55
|
+
import axios from 'axios';
|
|
56
|
+
import { getRouteParams, getAccessToken, closeApp, isInHost } from '@retion-miniapp/sdk';
|
|
57
|
+
|
|
58
|
+
/** Page + khách của conversation đang mở. */
|
|
59
|
+
export const conversation = getRouteParams();
|
|
60
|
+
|
|
61
|
+
/** HTTP client gọi backend của bạn — tự gắn token của page hiện tại. */
|
|
62
|
+
export const api = axios.create({ baseURL: import.meta.env.VITE_API_URL });
|
|
63
|
+
|
|
64
|
+
api.interceptors.request.use(async (config) => {
|
|
65
|
+
config.headers.Authorization = `Bearer ${await getAccessToken()}`;
|
|
66
|
+
return config;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/** Đóng mini app. Chạy trên web thì quay lại trang trước. */
|
|
70
|
+
export const close = () => (isInHost() ? closeApp() : history.back());
|
|
34
71
|
```
|
|
35
72
|
|
|
36
|
-
|
|
73
|
+
> Dùng `fetch` thay axios: gọi `await getAccessToken()` rồi gắn vào header y hệt.
|
|
74
|
+
|
|
75
|
+
## 4. Dùng trong component
|
|
76
|
+
|
|
77
|
+
```vue
|
|
78
|
+
<script setup lang="ts">
|
|
79
|
+
import { ref, onMounted } from 'vue';
|
|
80
|
+
import { api, conversation, close } from '@/lib/miniapp';
|
|
81
|
+
|
|
82
|
+
const orders = ref([]);
|
|
83
|
+
|
|
84
|
+
onMounted(async () => {
|
|
85
|
+
const { data } = await api.get('/orders', { params: { client_id: conversation.client_id } });
|
|
86
|
+
orders.value = data;
|
|
87
|
+
});
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<header>
|
|
92
|
+
<h1>Đơn hàng</h1>
|
|
93
|
+
</header>
|
|
94
|
+
<OrderList :orders="orders" />
|
|
95
|
+
<button @click="close">Xong</button>
|
|
96
|
+
</template>
|
|
97
|
+
```
|
|
37
98
|
|
|
38
|
-
|
|
99
|
+
Chừa khoảng **100px bên phải header** — app phủ nút `••• | ✕` ở góc trên phải.
|
|
39
100
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
101
|
+
## 5. Chạy thử trên trình duyệt
|
|
102
|
+
|
|
103
|
+
Truyền thông tin qua query URL như iframe hiện tại:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
http://localhost:5173/?page_id=100179064765476&client_id=8385907281421695&access_token=<token test>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`getRouteParams()` và `getAccessToken()` đọc từ URL này; `closeApp()` không chạy được ngoài app (hàm `close` ở trên sẽ `history.back()`).
|
|
110
|
+
|
|
111
|
+
## 6. Deploy
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
export RETION_MINIAPP_API=https://retion-miniapp-platform.vercel.app/api
|
|
115
|
+
export RETION_MINIAPP_TOKEN=<deploy token của app — lấy trên Console>
|
|
116
|
+
|
|
117
|
+
npx retion-miniapp deploy --channel testing # tự tăng version → build → upload
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Lên Console → version vừa deploy → **→ production**. Người dùng nhận bản mới ở lần mở mini app kế tiếp (app tải nền, không cần cập nhật app).
|
|
121
|
+
|
|
122
|
+
---
|
|
43
123
|
|
|
44
124
|
## API
|
|
45
125
|
|
|
46
|
-
|
|
|
47
|
-
|
|
48
|
-
| `getRouteParams()` | `RouteParams` |
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
| `closeApp()` | `void` | Đóng mini app |
|
|
56
|
-
| `openWebview({ url })` | `void` | Mở link http(s) |
|
|
57
|
-
| `showToast({ message })` | `void` | Toast native |
|
|
58
|
-
| `getSystemInfo()` | `SystemInfo` | `platform, appVersion, sdkVersion, inHost, theme` — ngoài app không lỗi |
|
|
59
|
-
| `isInHost()` | `boolean` | Đang chạy trong app Retion có hỗ trợ SDK |
|
|
126
|
+
| Hàm | Trả về | Trong app | Ngoài app (web / iframe) |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| `getRouteParams()` | `RouteParams` | Thông tin app truyền vào | Đọc query URL |
|
|
129
|
+
| `getAccessToken()` | `Promise<string>` | Token của page hiện tại (app tự refresh) | Đọc `?access_token=` — không có thì lỗi `NOT_IN_HOST` |
|
|
130
|
+
| `getToken(type)` | `Promise<string>` | `'access'` \| `'partner'` \| `'chatbox'` — `partner` / `chatbox` cần quyền (xem dưới) | Đọc `?access_token=` / `?partner_token=` / `?chatbox_token=` |
|
|
131
|
+
| `closeApp()` | `Promise<void>` | Đóng mini app | Lỗi `NOT_IN_HOST` |
|
|
132
|
+
| `isInHost()` | `boolean` | `true` | `false` |
|
|
133
|
+
|
|
134
|
+
`RouteParams`: `page_id`, `client_id`, `org_id`, `message_id`, `comment_id`, `locale`, `is_page_admin`.
|
|
60
135
|
|
|
61
136
|
### Lỗi
|
|
62
137
|
|
|
63
|
-
Mọi lỗi là `MiniAppError`
|
|
138
|
+
Mọi lỗi là `MiniAppError` có `code`:
|
|
64
139
|
|
|
65
|
-
| code | Khi nào |
|
|
140
|
+
| `code` | Khi nào |
|
|
66
141
|
|---|---|
|
|
67
|
-
| `NOT_IN_HOST` | Chạy ngoài app
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `HOST_ERROR` | Lỗi khác phía app |
|
|
142
|
+
| `NOT_IN_HOST` | Chạy ngoài app mà gọi hàm cần app |
|
|
143
|
+
| `PERMISSION_DENIED` | Mini app chưa được cấp quyền lấy token đó |
|
|
144
|
+
| `TIMEOUT` | App không phản hồi sau 15s |
|
|
145
|
+
| `NOT_SUPPORTED` | Phiên bản app hiện tại chưa hỗ trợ |
|
|
146
|
+
| `HOST_ERROR` | Lỗi khác phía app (vd page chưa có token) |
|
|
73
147
|
|
|
74
|
-
```
|
|
75
|
-
import {
|
|
148
|
+
```ts
|
|
149
|
+
import { getAccessToken, MiniAppError, ErrorCode } from '@retion-miniapp/sdk';
|
|
76
150
|
|
|
77
151
|
try {
|
|
78
|
-
await
|
|
152
|
+
await getAccessToken();
|
|
79
153
|
} catch (e) {
|
|
80
|
-
if (e instanceof MiniAppError && e.code === ErrorCode.
|
|
154
|
+
if (e instanceof MiniAppError && e.code === ErrorCode.HOST_ERROR) showLoginAgain();
|
|
81
155
|
}
|
|
82
156
|
```
|
|
83
157
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
```js
|
|
87
|
-
import { useRouteParams } from '@retion-miniapp/sdk/vue';
|
|
158
|
+
### Token khác ngoài `access_token`
|
|
88
159
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
160
|
+
| Token | Là gì | Cần quyền |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `access` | Token của mini app cho page hiện tại | Không — luôn được |
|
|
163
|
+
| `partner` | Token đối tác của page | `partner_token` |
|
|
164
|
+
| `chatbox` | Token đăng nhập của người dùng app (**toàn quyền** như người dùng) | `chatbox_token` |
|
|
94
165
|
|
|
95
|
-
|
|
96
|
-
import { setMockHandler } from '@retion-miniapp/sdk';
|
|
166
|
+
Quyền do **admin bật trên Console** (app → *Quyền lấy token*) — không khai trong code được. Chưa bật → `getToken()` lỗi `PERMISSION_DENIED`, và token đó cũng **không có trên URL**.
|
|
97
167
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (method === 'getAccessToken') return 'dev-token';
|
|
101
|
-
if (method === 'getPageInfo') return { page_id: 'p1', name: 'Page test', platform: 'facebook' };
|
|
102
|
-
});
|
|
103
|
-
}
|
|
168
|
+
```ts
|
|
169
|
+
const partnerToken = await getToken('partner');
|
|
104
170
|
```
|
|
105
171
|
|
|
106
|
-
##
|
|
172
|
+
## Hỏi đáp
|
|
107
173
|
|
|
108
|
-
|
|
174
|
+
**Web đang chạy (iframe / trình duyệt) có bị ảnh hưởng khi cài SDK?**
|
|
175
|
+
Không. Ngoài app SDK không gọi mạng, không đụng DOM; `getRouteParams` / `getAccessToken` đọc query URL như trước.
|
|
109
176
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
npx retion-miniapp deploy
|
|
113
|
-
```
|
|
177
|
+
**Token hết hạn?**
|
|
178
|
+
Gọi `getAccessToken()` mỗi lần cần (interceptor ở mục 3 đã làm vậy) — app tự trả token còn hạn. Không lưu token vào localStorage.
|
|
114
179
|
|
|
115
|
-
|
|
180
|
+
**Mở mini app ở page khác?**
|
|
181
|
+
Mỗi page là 1 phiên mini app riêng (token, dữ liệu riêng). Code không cần xử lý gì.
|
|
116
182
|
|
|
117
|
-
|
|
183
|
+
**Có phải tải lại mỗi lần mở?**
|
|
184
|
+
Code mini app nằm sẵn trên máy, chỉ tải khi có version mới. Đóng rồi mở lại trong lúc app còn chạy → hiện ngay.
|
|
118
185
|
|
|
119
|
-
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Phát triển SDK
|
|
120
189
|
|
|
121
190
|
```bash
|
|
122
191
|
npm i
|
|
123
|
-
npm run check
|
|
124
|
-
npm run release:
|
|
192
|
+
npm run check # typecheck + lint + test + build + publint
|
|
193
|
+
npm run release:minor # ghi CHANGELOG mục "## Unreleased" trước
|
|
125
194
|
```
|
|
126
195
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
1. Ghi thay đổi vào `CHANGELOG.md` dưới `## Unreleased`.
|
|
130
|
-
2. `npm run release:patch` → chạy `check` → tăng version → cập nhật CHANGELOG → commit + tag `vX.Y.Z` → push.
|
|
131
|
-
3. GitHub Actions (`release.yml`) thấy tag → publish lên npm (cần secret `NPM_TOKEN`).
|
|
196
|
+
Giao thức với app: xem chú thích đầu [`src/bridge.ts`](./src/bridge.ts).
|
|
132
197
|
|
|
133
198
|
## License
|
|
134
199
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,91 +1,187 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var ErrorCode = {
|
|
5
|
+
/** Không chạy trong app Retion (trình duyệt / iframe cũ). */
|
|
6
|
+
NOT_IN_HOST: "NOT_IN_HOST",
|
|
7
|
+
/** Host không phản hồi trong thời gian chờ. */
|
|
8
|
+
TIMEOUT: "TIMEOUT",
|
|
9
|
+
/** Mini app chưa được admin cấp quyền (vd lấy partner_token / chatbox_token). */
|
|
10
|
+
PERMISSION_DENIED: "PERMISSION_DENIED",
|
|
11
|
+
/** Host (phiên bản app hiện tại) chưa hỗ trợ API này. */
|
|
12
|
+
NOT_SUPPORTED: "NOT_SUPPORTED",
|
|
13
|
+
/** Lỗi khác phía host. */
|
|
14
|
+
HOST_ERROR: "HOST_ERROR"
|
|
15
|
+
};
|
|
16
|
+
var MiniAppError = class _MiniAppError extends Error {
|
|
17
|
+
constructor(code, message, method) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "MiniAppError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.method = method;
|
|
22
|
+
}
|
|
23
|
+
/** Chuẩn hoá lỗi từ host (object thuần, Error, string) về MiniAppError. */
|
|
24
|
+
static from(error, method) {
|
|
25
|
+
if (error instanceof _MiniAppError) {
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
if (error && typeof error === "object") {
|
|
29
|
+
const { code, message } = error;
|
|
30
|
+
return new _MiniAppError(
|
|
31
|
+
typeof code === "string" ? code : ErrorCode.HOST_ERROR,
|
|
32
|
+
typeof message === "string" ? message : "Unknown error",
|
|
33
|
+
method
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return new _MiniAppError(ErrorCode.HOST_ERROR, String(error), method);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
4
39
|
|
|
5
|
-
// src/
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
40
|
+
// src/bridge.ts
|
|
41
|
+
var PROTOCOL_VERSION = 1;
|
|
42
|
+
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
43
|
+
var CONTEXT_EVENT = "context";
|
|
44
|
+
var hasWindow = typeof window !== "undefined";
|
|
45
|
+
var readInitialContext = () => {
|
|
46
|
+
var _a;
|
|
47
|
+
if (!hasWindow) {
|
|
48
|
+
return {};
|
|
13
49
|
}
|
|
14
|
-
|
|
50
|
+
const query = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
51
|
+
return { ...query, ...(_a = window.__RETION_MINIAPP_CONTEXT__) != null ? _a : {} };
|
|
15
52
|
};
|
|
16
|
-
var
|
|
17
|
-
const
|
|
18
|
-
|
|
53
|
+
var createState = () => {
|
|
54
|
+
const state = {
|
|
55
|
+
protocol: PROTOCOL_VERSION,
|
|
56
|
+
seq: 0,
|
|
57
|
+
pending: /* @__PURE__ */ new Map(),
|
|
58
|
+
listeners: /* @__PURE__ */ new Map(),
|
|
59
|
+
context: readInitialContext(),
|
|
60
|
+
receive: (message) => {
|
|
61
|
+
if (!message || typeof message !== "object") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if ("event" in message) {
|
|
65
|
+
if (message.event === CONTEXT_EVENT) {
|
|
66
|
+
const prev = state.context;
|
|
67
|
+
state.context = { ...prev, ...message.data };
|
|
68
|
+
emit(state, CONTEXT_EVENT, { next: { ...state.context }, prev });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
emit(state, message.event, message.data);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const pending = state.pending.get(message.id);
|
|
75
|
+
if (!pending) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
state.pending.delete(message.id);
|
|
79
|
+
clearTimeout(pending.timer);
|
|
80
|
+
if (message.error) {
|
|
81
|
+
pending.reject(MiniAppError.from(message.error, pending.method));
|
|
82
|
+
} else {
|
|
83
|
+
pending.resolve(message.result);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
return state;
|
|
19
88
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
89
|
+
var emit = (state, event, data) => {
|
|
90
|
+
var _a;
|
|
91
|
+
(_a = state.listeners.get(event)) == null ? void 0 : _a.forEach((listener) => {
|
|
92
|
+
try {
|
|
93
|
+
listener(data);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`[retion-miniapp] listener "${event}" l\u1ED7i`, error);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
var fallback_state = null;
|
|
100
|
+
var getState = () => {
|
|
101
|
+
var _a;
|
|
102
|
+
if (!hasWindow) {
|
|
103
|
+
return fallback_state != null ? fallback_state : fallback_state = createState();
|
|
33
104
|
}
|
|
34
|
-
return
|
|
105
|
+
return (_a = window.__RETION_MINIAPP__) != null ? _a : window.__RETION_MINIAPP__ = createState();
|
|
106
|
+
};
|
|
107
|
+
var isInHost = () => {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
return hasWindow && typeof ((_a = window.__RETION_MINIAPP_HOST__) == null ? void 0 : _a.protocol) === "number" && typeof ((_b = window.ReactNativeWebView) == null ? void 0 : _b.postMessage) === "function";
|
|
35
110
|
};
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
return invoke("showToast", params, callbacks);
|
|
111
|
+
var post = (message) => {
|
|
112
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
39
113
|
};
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
(_a = options == null ? void 0 : options.success) == null ? void 0 : _a.call(options, base);
|
|
45
|
-
return base;
|
|
114
|
+
var call = (method, params, timeout_ms = DEFAULT_TIMEOUT_MS) => {
|
|
115
|
+
const state = getState();
|
|
116
|
+
if (!isInHost()) {
|
|
117
|
+
return Promise.reject(new MiniAppError(ErrorCode.NOT_IN_HOST, `${method} ch\u1EC9 ch\u1EA1y trong app Retion`, method));
|
|
46
118
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
(
|
|
55
|
-
|
|
119
|
+
const id = `${Date.now().toString(36)}-${++state.seq}`;
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
const timer = setTimeout(() => {
|
|
122
|
+
state.pending.delete(id);
|
|
123
|
+
reject(new MiniAppError(ErrorCode.TIMEOUT, `${method}: host kh\xF4ng ph\u1EA3n h\u1ED3i sau ${timeout_ms}ms`, method));
|
|
124
|
+
}, timeout_ms);
|
|
125
|
+
state.pending.set(id, { method, resolve, reject, timer });
|
|
126
|
+
post({ __retion: PROTOCOL_VERSION, id, method, params });
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
var notify = (method, params) => {
|
|
130
|
+
if (isInHost()) {
|
|
131
|
+
post({ __retion: PROTOCOL_VERSION, method, params });
|
|
56
132
|
}
|
|
57
133
|
};
|
|
58
|
-
var notifyReady = () => chunkC2AZ33LI_cjs.notify("ready", { sdkVersion: SDK_VERSION });
|
|
59
134
|
|
|
60
|
-
// src/
|
|
61
|
-
var
|
|
62
|
-
|
|
135
|
+
// src/version.ts
|
|
136
|
+
var SDK_VERSION = "0.3.0" ;
|
|
137
|
+
|
|
138
|
+
// src/apis/app.ts
|
|
139
|
+
var closeApp = () => call("closeApp");
|
|
140
|
+
var notifyReady = () => notify("ready", { sdkVersion: SDK_VERSION });
|
|
63
141
|
|
|
64
|
-
// src/apis/
|
|
65
|
-
var
|
|
142
|
+
// src/apis/route.ts
|
|
143
|
+
var getRouteParams = () => ({ ...getState().context });
|
|
66
144
|
|
|
67
|
-
// src/apis/
|
|
68
|
-
var
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const error = new chunkC2AZ33LI_cjs.MiniAppError(chunkC2AZ33LI_cjs.ErrorCode.INVALID_PARAMS, "text kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng", method);
|
|
73
|
-
(_a = callbacks.fail) == null ? void 0 : _a.call(callbacks, error);
|
|
74
|
-
return Promise.reject(error);
|
|
145
|
+
// src/apis/user.ts
|
|
146
|
+
var TOKEN_QUERY_PARAM = "access_token";
|
|
147
|
+
var readTokenFromUrl = () => {
|
|
148
|
+
if (typeof window === "undefined") {
|
|
149
|
+
return null;
|
|
75
150
|
}
|
|
76
|
-
return
|
|
151
|
+
return new URLSearchParams(window.location.search).get(TOKEN_QUERY_PARAM);
|
|
77
152
|
};
|
|
78
|
-
var
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
153
|
+
var getAccessToken = async () => {
|
|
154
|
+
if (isInHost()) {
|
|
155
|
+
return call("getAccessToken");
|
|
156
|
+
}
|
|
157
|
+
const token = readTokenFromUrl();
|
|
158
|
+
if (!token) {
|
|
159
|
+
throw new MiniAppError(ErrorCode.NOT_IN_HOST, `Ngo\xE0i app: thi\u1EBFu ?${TOKEN_QUERY_PARAM}= tr\xEAn URL`, "getAccessToken");
|
|
160
|
+
}
|
|
161
|
+
return token;
|
|
162
|
+
};
|
|
163
|
+
var QUERY_PARAM = {
|
|
164
|
+
access: "access_token",
|
|
165
|
+
partner: "partner_token",
|
|
166
|
+
chatbox: "chatbox_token"
|
|
167
|
+
};
|
|
168
|
+
var getToken = async (type) => {
|
|
169
|
+
if (!(type in QUERY_PARAM)) {
|
|
170
|
+
throw new MiniAppError(ErrorCode.HOST_ERROR, `Lo\u1EA1i token kh\xF4ng h\u1EE3p l\u1EC7: ${String(type)}`, "getToken");
|
|
171
|
+
}
|
|
172
|
+
if (isInHost()) {
|
|
173
|
+
return call("getToken", { type });
|
|
174
|
+
}
|
|
175
|
+
const token = typeof window === "undefined" ? null : new URLSearchParams(window.location.search).get(QUERY_PARAM[type]);
|
|
176
|
+
if (!token) {
|
|
177
|
+
throw new MiniAppError(ErrorCode.NOT_IN_HOST, `Ngo\xE0i app: thi\u1EBFu ?${QUERY_PARAM[type]}= tr\xEAn URL`, "getToken");
|
|
178
|
+
}
|
|
179
|
+
return token;
|
|
84
180
|
};
|
|
85
181
|
|
|
86
182
|
// src/index.ts
|
|
87
183
|
if (typeof window !== "undefined") {
|
|
88
|
-
|
|
184
|
+
getState();
|
|
89
185
|
if (document.readyState === "loading") {
|
|
90
186
|
document.addEventListener("DOMContentLoaded", notifyReady, { once: true });
|
|
91
187
|
} else {
|
|
@@ -93,40 +189,13 @@ if (typeof window !== "undefined") {
|
|
|
93
189
|
}
|
|
94
190
|
}
|
|
95
191
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
get: function () { return chunkC2AZ33LI_cjs.ErrorCode; }
|
|
99
|
-
});
|
|
100
|
-
Object.defineProperty(exports, "MiniAppError", {
|
|
101
|
-
enumerable: true,
|
|
102
|
-
get: function () { return chunkC2AZ33LI_cjs.MiniAppError; }
|
|
103
|
-
});
|
|
104
|
-
Object.defineProperty(exports, "PROTOCOL_VERSION", {
|
|
105
|
-
enumerable: true,
|
|
106
|
-
get: function () { return chunkC2AZ33LI_cjs.PROTOCOL_VERSION; }
|
|
107
|
-
});
|
|
108
|
-
Object.defineProperty(exports, "getRouteParams", {
|
|
109
|
-
enumerable: true,
|
|
110
|
-
get: function () { return chunkC2AZ33LI_cjs.getRouteParams; }
|
|
111
|
-
});
|
|
112
|
-
Object.defineProperty(exports, "isInHost", {
|
|
113
|
-
enumerable: true,
|
|
114
|
-
get: function () { return chunkC2AZ33LI_cjs.isInHost; }
|
|
115
|
-
});
|
|
116
|
-
Object.defineProperty(exports, "onContextChange", {
|
|
117
|
-
enumerable: true,
|
|
118
|
-
get: function () { return chunkC2AZ33LI_cjs.onContextChange; }
|
|
119
|
-
});
|
|
192
|
+
exports.ErrorCode = ErrorCode;
|
|
193
|
+
exports.MiniAppError = MiniAppError;
|
|
120
194
|
exports.SDK_VERSION = SDK_VERSION;
|
|
121
195
|
exports.closeApp = closeApp;
|
|
122
196
|
exports.getAccessToken = getAccessToken;
|
|
123
|
-
exports.
|
|
124
|
-
exports.
|
|
125
|
-
exports.
|
|
126
|
-
exports.insertText = insertText;
|
|
127
|
-
exports.openWebview = openWebview;
|
|
128
|
-
exports.sendMessage = sendMessage;
|
|
129
|
-
exports.setMockHandler = setMockHandler;
|
|
130
|
-
exports.showToast = showToast;
|
|
197
|
+
exports.getRouteParams = getRouteParams;
|
|
198
|
+
exports.getToken = getToken;
|
|
199
|
+
exports.isInHost = isInHost;
|
|
131
200
|
//# sourceMappingURL=index.cjs.map
|
|
132
201
|
//# sourceMappingURL=index.cjs.map
|