@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 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
- - Bridge giao thức v1 với host React Native (request/response theo id, timeout, event).
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
- SDK cho web mini app chạy trong app Retion. Bridge với app được ẩn hoàn toàn — chỉ cần import hàm.
3
+ Kết nối web mini app (Vue, React, ...) với app Retion.
4
4
 
5
- - Chạy được ở **cả web lẫn app**: ngoài app, SDK không lỗi, `getRouteParams()` đọc query URL, API cần app reject `NOT_IN_HOST`.
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
- ## Cài đặt
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 # build + deploy
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
- ## Dùng nhanh
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
- ```js
19
- import { getRouteParams, getAccessToken, insertText, closeApp, isInHost } from '@retion-miniapp/sdk';
25
+ ## 2. Cấu hình project
20
26
 
21
- const { page_id, client_id } = getRouteParams();
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
- const token = await getAccessToken();
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
- if (isInHost()) {
29
- await insertText({ text: `Đơn #${order.id}: ${order.total}đ` });
30
- await closeApp();
31
- } else {
32
- router.back(); // chạy trên web: giữ hành vi cũ
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
- ## Mini app theo page
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
- Mini app được cài theo **page** (kênh). Mỗi page có token, quyền, dữ liệu riêng — app chạy **1 instance mini app cho mỗi page**:
99
+ Chừa khoảng **100px bên phải header** — app phủ nút `••• | ✕` ở góc trên phải.
39
100
 
40
- - Đổi conversation **trong cùng page** → `onContextChange` (nếu `context_update: true`), hoặc reload với query mới.
41
- - Đổi **page** → instance khác, mini app khởi động lại với `page_id` mới. Không cần tự xử lý.
42
- - `getAccessToken()` luôn trả token của page hiện tại — gọi lại mỗi khi cần, không lưu lâu.
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
- | API | Trả về | Mô tả |
47
- |---|---|---|
48
- | `getRouteParams()` | `RouteParams` | `page_id, client_id, org_id, message_id, locale, is_page_admin, ...` (đồng bộ) |
49
- | `onContextChange(cb)` | `() => void` | Đổi conversation trong cùng page mà không reload |
50
- | `getPageInfo()` | `PageInfo` | `{ page_id, name, avatar, platform }` |
51
- | `getAccessToken()` | `string` | Token của page hiện tại |
52
- | `getUserInfo()` | `UserInfo` | Nhân viên đang đăng nhập |
53
- | `sendMessage({ text })` | `void` | Gửi tin vào conversation |
54
- | `insertText({ text })` | `void` | Chèn text vào ô nhập chat |
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` với `code`:
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 (web / màn iframe cũ của app) và không có mock |
68
- | `TIMEOUT` | Host không phản hồi (15s) |
69
- | `NOT_SUPPORTED` | App version hiện tại chưa hỗ trợ API |
70
- | `INVALID_PARAMS` | Tham số sai (url không phải http(s), text rỗng, ...) |
71
- | `USER_CANCELLED` | User huỷ thao tác |
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
- ```js
75
- import { closeApp, MiniAppError, ErrorCode } from '@retion-miniapp/sdk';
148
+ ```ts
149
+ import { getAccessToken, MiniAppError, ErrorCode } from '@retion-miniapp/sdk';
76
150
 
77
151
  try {
78
- await closeApp();
152
+ await getAccessToken();
79
153
  } catch (e) {
80
- if (e instanceof MiniAppError && e.code === ErrorCode.NOT_IN_HOST) { /* đang chạy trên web */ }
154
+ if (e instanceof MiniAppError && e.code === ErrorCode.HOST_ERROR) showLoginAgain();
81
155
  }
82
156
  ```
83
157
 
84
- ## Vue
85
-
86
- ```js
87
- import { useRouteParams } from '@retion-miniapp/sdk/vue';
158
+ ### Token khác ngoài `access_token`
88
159
 
89
- const params = useRouteParams(); // ShallowRef<RouteParams>, tự cập nhật khi đổi conversation
90
- watch(() => params.value.client_id, fetchData, { immediate: true });
91
- ```
92
-
93
- ## Dev trên trình duyệt
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
- ```js
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
- if (import.meta.env.DEV) {
99
- setMockHandler((method, params) => {
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
- ## Build cho app
172
+ ## Hỏi đáp
107
173
 
108
- `vite.config`: `base: './'` (chạy offline từ file local). Sau đó dùng [`@retion-miniapp/cli`](https://www.npmjs.com/package/@retion-miniapp/cli):
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
- ```bash
111
- npx retion-miniapp init
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
- ## Giao thức (cho team app)
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
- Xem chú thích đầu [`src/bridge.ts`](./src/bridge.ts). Host phải inject `window.__RETION_MINIAPP_HOST__ = { protocol: 1 }` trước khi trang chạy — thiếu marker, SDK coi như chạy ngoài app.
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
- ## Phát triển
186
+ ---
187
+
188
+ ## Phát triển SDK
120
189
 
121
190
  ```bash
122
191
  npm i
123
- npm run check # typecheck + lint + test + build + publint
124
- npm run release:patch # (hoặc minor/major) — xem mục 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
- ### Release
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
- var chunkC2AZ33LI_cjs = require('./chunk-C2AZ33LI.cjs');
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/invoke.ts
6
- var invoke = (method, params, options) => {
7
- const promise = chunkC2AZ33LI_cjs.call(method, params);
8
- if ((options == null ? void 0 : options.success) || (options == null ? void 0 : options.fail)) {
9
- promise.then(options.success, (error) => {
10
- var _a;
11
- return (_a = options.fail) == null ? void 0 : _a.call(options, chunkC2AZ33LI_cjs.MiniAppError.from(error, method));
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
- return promise;
50
+ const query = Object.fromEntries(new URLSearchParams(window.location.search));
51
+ return { ...query, ...(_a = window.__RETION_MINIAPP_CONTEXT__) != null ? _a : {} };
15
52
  };
16
- var splitOptions = (options) => {
17
- const { success, fail, ...params } = options;
18
- return [params, { success, fail }];
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
- // src/version.ts
22
- var SDK_VERSION = "0.1.0" ;
23
-
24
- // src/apis/app.ts
25
- var closeApp = (options) => invoke("closeApp", void 0, options);
26
- var openWebview = (options) => {
27
- var _a, _b;
28
- const [params, callbacks] = splitOptions(options);
29
- if (!/^https?:\/\//i.test((_a = params.url) != null ? _a : "")) {
30
- const error = new chunkC2AZ33LI_cjs.MiniAppError(chunkC2AZ33LI_cjs.ErrorCode.INVALID_PARAMS, "url ph\u1EA3i b\u1EAFt \u0111\u1EA7u b\u1EB1ng http:// ho\u1EB7c https://", "openWebview");
31
- (_b = callbacks.fail) == null ? void 0 : _b.call(callbacks, error);
32
- return Promise.reject(error);
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 invoke("openWebview", params, callbacks);
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 showToast = (options) => {
37
- const [params, callbacks] = splitOptions(options);
38
- return invoke("showToast", params, callbacks);
111
+ var post = (message) => {
112
+ window.ReactNativeWebView.postMessage(JSON.stringify(message));
39
113
  };
40
- var getSystemInfo = async (options) => {
41
- var _a, _b, _c;
42
- const base = { platform: "web", sdkVersion: SDK_VERSION, inHost: chunkC2AZ33LI_cjs.isInHost() };
43
- if (!chunkC2AZ33LI_cjs.isInHost()) {
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
- try {
48
- const host = await invoke("getSystemInfo");
49
- const result = { ...base, ...host, sdkVersion: SDK_VERSION, inHost: true };
50
- (_b = options == null ? void 0 : options.success) == null ? void 0 : _b.call(options, result);
51
- return result;
52
- } catch (error) {
53
- const normalized = chunkC2AZ33LI_cjs.MiniAppError.from(error, "getSystemInfo");
54
- (_c = options == null ? void 0 : options.fail) == null ? void 0 : _c.call(options, normalized);
55
- throw normalized;
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/apis/user.ts
61
- var getAccessToken = (options) => invoke("getAccessToken", void 0, options);
62
- var getUserInfo = (options) => invoke("getUserInfo", void 0, options);
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/page.ts
65
- var getPageInfo = (options) => invoke("getPageInfo", void 0, options);
142
+ // src/apis/route.ts
143
+ var getRouteParams = () => ({ ...getState().context });
66
144
 
67
- // src/apis/chat.ts
68
- var withText = (method, options) => {
69
- var _a;
70
- const [params, callbacks] = splitOptions(options);
71
- if (typeof params.text !== "string" || !params.text.trim()) {
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 invoke(method, params, callbacks);
151
+ return new URLSearchParams(window.location.search).get(TOKEN_QUERY_PARAM);
77
152
  };
78
- var sendMessage = (options) => withText("sendMessage", options);
79
- var insertText = (options) => withText("insertText", options);
80
-
81
- // src/apis/dev.ts
82
- var setMockHandler = (handler) => {
83
- chunkC2AZ33LI_cjs.getState().mock = handler;
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
- chunkC2AZ33LI_cjs.getState();
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
- Object.defineProperty(exports, "ErrorCode", {
97
- enumerable: true,
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.getPageInfo = getPageInfo;
124
- exports.getSystemInfo = getSystemInfo;
125
- exports.getUserInfo = getUserInfo;
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