@cloudparse/up-miniapps-sdk 0.5.7 → 0.6.1
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/bin/up-sdk.cjs +99 -0
- package/dist/index.browser.min.js +23 -23
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +266 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +222 -1
- package/dist/index.d.ts +222 -1
- package/dist/index.js +264 -10
- package/dist/index.js.map +1 -1
- package/package.json +12 -5
package/dist/index.js
CHANGED
|
@@ -62,7 +62,16 @@ var Button = React.forwardRef(
|
|
|
62
62
|
);
|
|
63
63
|
Button.displayName = "Button";
|
|
64
64
|
|
|
65
|
+
// src/utils/environment.ts
|
|
66
|
+
function isRunningInsideNativeShell() {
|
|
67
|
+
if (typeof window === "undefined") return false;
|
|
68
|
+
const w = window;
|
|
69
|
+
return w.__UP_REACT_NATIVE_APP__ === true || !!w.ReactNativeWebView;
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
// src/bridge.ts
|
|
73
|
+
var BridgeTimeoutError = class extends Error {
|
|
74
|
+
};
|
|
66
75
|
var Bridge = class {
|
|
67
76
|
callbacks = /* @__PURE__ */ new Map();
|
|
68
77
|
eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -133,18 +142,32 @@ var Bridge = class {
|
|
|
133
142
|
* @param type The action type (e.g., "GET_LOCATION").
|
|
134
143
|
* @param payload Data associated with the action.
|
|
135
144
|
* @param timeoutMs Time in milliseconds to wait for a response before rejecting.
|
|
145
|
+
* @param retries Number of additional attempts if a request times out (default 1 — one retry).
|
|
146
|
+
* Exists because the response for a request can be lost in transit — e.g. the host's
|
|
147
|
+
* injected response script running before this WebView's own `message` listener has
|
|
148
|
+
* attached, worse under rapid mount/unmount cycles — with no application-visible symptom
|
|
149
|
+
* other than the full 30s timeout. A blind retry self-heals that class of loss without
|
|
150
|
+
* needing to diagnose which specific transport race dropped the original message; every
|
|
151
|
+
* call site benefits automatically instead of each consumer app hand-rolling its own
|
|
152
|
+
* retry-after-catch logic (which previously only covered `initialize()`, and imperfectly).
|
|
136
153
|
* @returns A promise that resolves with the native response.
|
|
137
154
|
*/
|
|
138
|
-
send(type, payload, timeoutMs = 3e4) {
|
|
155
|
+
send(type, payload, timeoutMs = 3e4, retries = 1) {
|
|
139
156
|
if (!this.isNative()) {
|
|
140
157
|
return this.handleMockMessage(type, payload);
|
|
141
158
|
}
|
|
159
|
+
return this.sendOnce(type, payload, timeoutMs).catch((err) => {
|
|
160
|
+
if (retries <= 0 || !(err instanceof BridgeTimeoutError)) throw err;
|
|
161
|
+
return this.send(type, payload, timeoutMs, retries - 1);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
sendOnce(type, payload, timeoutMs) {
|
|
142
165
|
return new Promise((resolve, reject) => {
|
|
143
166
|
const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
144
167
|
const timeout = setTimeout(() => {
|
|
145
168
|
if (this.callbacks.has(callbackId)) {
|
|
146
169
|
this.callbacks.delete(callbackId);
|
|
147
|
-
reject(new
|
|
170
|
+
reject(new BridgeTimeoutError(`Bridge request timeout: ${type}`));
|
|
148
171
|
}
|
|
149
172
|
}, timeoutMs);
|
|
150
173
|
this.callbacks.set(callbackId, {
|
|
@@ -218,8 +241,7 @@ var Bridge = class {
|
|
|
218
241
|
* Determine environment. Check flag or cache result.
|
|
219
242
|
*/
|
|
220
243
|
isNative() {
|
|
221
|
-
|
|
222
|
-
return window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView;
|
|
244
|
+
return isRunningInsideNativeShell();
|
|
223
245
|
}
|
|
224
246
|
};
|
|
225
247
|
|
|
@@ -1979,14 +2001,14 @@ var useUserLocation = () => {
|
|
|
1979
2001
|
});
|
|
1980
2002
|
const mapPayload = useCallback3((payload) => {
|
|
1981
2003
|
const coords = payload.location?.coords || payload.coords;
|
|
1982
|
-
const headingData = payload.heading
|
|
2004
|
+
const headingData = payload.heading;
|
|
1983
2005
|
const newCoords = coords ? [coords.longitude, coords.latitude] : null;
|
|
1984
2006
|
let heading = null;
|
|
1985
2007
|
if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
|
|
1986
2008
|
heading = headingData.trueHeading;
|
|
1987
2009
|
} else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
|
|
1988
2010
|
heading = headingData.magneticHeading;
|
|
1989
|
-
} else if (headingData?.heading !== null && headingData
|
|
2011
|
+
} else if (headingData?.heading !== null && headingData?.heading >= 0) {
|
|
1990
2012
|
heading = headingData.heading;
|
|
1991
2013
|
}
|
|
1992
2014
|
return {
|
|
@@ -2463,7 +2485,14 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
2463
2485
|
var MockRadiusLayer = ({
|
|
2464
2486
|
center,
|
|
2465
2487
|
radiusMeters,
|
|
2466
|
-
color
|
|
2488
|
+
// Neutral gray, not any one app's accent color — this debug aid is shared
|
|
2489
|
+
// across every mini-app (each with its own, different palette), so it
|
|
2490
|
+
// shouldn't default to a color that reads as "part of the app" (was
|
|
2491
|
+
// #39ff88, a neon green/yellow left over from an earlier shared theme,
|
|
2492
|
+
// reported as looking like an unwanted yellow border around the user's
|
|
2493
|
+
// position marker in up-miniapps-explore, which has since moved to a
|
|
2494
|
+
// pastel palette with no neon accents at all).
|
|
2495
|
+
color = "#e2e8f0"
|
|
2467
2496
|
}) => {
|
|
2468
2497
|
const data = useMemo4(
|
|
2469
2498
|
() => buildGeoCircle(center, radiusMeters),
|
|
@@ -3458,7 +3487,7 @@ var loadImage2 = (src) => new Promise((resolve, reject) => {
|
|
|
3458
3487
|
});
|
|
3459
3488
|
|
|
3460
3489
|
// package.json
|
|
3461
|
-
var version = "0.
|
|
3490
|
+
var version = "0.6.1";
|
|
3462
3491
|
|
|
3463
3492
|
// src/sdk.ts
|
|
3464
3493
|
function deepFreeze(obj) {
|
|
@@ -3473,6 +3502,7 @@ function deepFreeze(obj) {
|
|
|
3473
3502
|
return Object.freeze(obj);
|
|
3474
3503
|
}
|
|
3475
3504
|
var globalRoutingConfig = null;
|
|
3505
|
+
var lastKnownEnv = null;
|
|
3476
3506
|
var MiniAppSDK = class {
|
|
3477
3507
|
/**
|
|
3478
3508
|
* Map services.
|
|
@@ -3795,7 +3825,14 @@ var MiniAppSDK = class {
|
|
|
3795
3825
|
});
|
|
3796
3826
|
constructor() {
|
|
3797
3827
|
this.bridge = new Bridge();
|
|
3828
|
+
this.bridge.on(
|
|
3829
|
+
"UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
|
|
3830
|
+
(data) => {
|
|
3831
|
+
if (data?.env) lastKnownEnv = data.env;
|
|
3832
|
+
}
|
|
3833
|
+
);
|
|
3798
3834
|
this.initialize = this.initialize.bind(this);
|
|
3835
|
+
this.getEnv = this.getEnv.bind(this);
|
|
3799
3836
|
this.on = this.on.bind(this);
|
|
3800
3837
|
this.off = this.off.bind(this);
|
|
3801
3838
|
}
|
|
@@ -3819,8 +3856,22 @@ var MiniAppSDK = class {
|
|
|
3819
3856
|
* The starting point for the Mini-App. Must be called before any other SDK methods.
|
|
3820
3857
|
* @param options The options to provide to the shell app
|
|
3821
3858
|
*/
|
|
3822
|
-
initialize(options) {
|
|
3823
|
-
|
|
3859
|
+
async initialize(options) {
|
|
3860
|
+
const res = await this.bridge.send(
|
|
3861
|
+
"INITIALIZE",
|
|
3862
|
+
options
|
|
3863
|
+
);
|
|
3864
|
+
if (res?.env) lastKnownEnv = res.env;
|
|
3865
|
+
return res;
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Returns the last known `env` (shell-provided config like `backendUrl`),
|
|
3869
|
+
* refreshed automatically by `initialize()` and every `UPDATE_CONTEXT`
|
|
3870
|
+
* push — read this instead of caching your own copy of `env` values.
|
|
3871
|
+
* Returns `null` before the first successful `initialize()`/context push.
|
|
3872
|
+
*/
|
|
3873
|
+
getEnv() {
|
|
3874
|
+
return lastKnownEnv;
|
|
3824
3875
|
}
|
|
3825
3876
|
/**
|
|
3826
3877
|
* Registers a callback for a specific event from the native shell.
|
|
@@ -4967,6 +5018,207 @@ var SwipeCarousel = ({
|
|
|
4967
5018
|
);
|
|
4968
5019
|
};
|
|
4969
5020
|
|
|
5021
|
+
// src/services/apiService.ts
|
|
5022
|
+
function buildUrl(baseUrl, path) {
|
|
5023
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
5024
|
+
return path;
|
|
5025
|
+
}
|
|
5026
|
+
let cleanBase = baseUrl.trim();
|
|
5027
|
+
while (cleanBase.endsWith("/")) {
|
|
5028
|
+
cleanBase = cleanBase.slice(0, -1);
|
|
5029
|
+
}
|
|
5030
|
+
let cleanPath = path.trim();
|
|
5031
|
+
if (!cleanPath.startsWith("/")) {
|
|
5032
|
+
cleanPath = `/${cleanPath}`;
|
|
5033
|
+
}
|
|
5034
|
+
const baseEndsWithV1 = cleanBase.endsWith("/api/v1");
|
|
5035
|
+
const pathStartsWithV1 = cleanPath.startsWith("/api/v1/") || cleanPath === "/api/v1";
|
|
5036
|
+
if (baseEndsWithV1 && pathStartsWithV1) {
|
|
5037
|
+
const strippedPath = cleanPath.slice(7);
|
|
5038
|
+
return `${cleanBase}${strippedPath}`;
|
|
5039
|
+
}
|
|
5040
|
+
return `${cleanBase}${cleanPath}`;
|
|
5041
|
+
}
|
|
5042
|
+
function getEarlyBackendUrl(kind) {
|
|
5043
|
+
if (typeof window === "undefined") return void 0;
|
|
5044
|
+
const early = window.__UP_BACKEND_URLS__;
|
|
5045
|
+
return early?.[kind] ?? void 0;
|
|
5046
|
+
}
|
|
5047
|
+
function createApiService(options) {
|
|
5048
|
+
const getBaseUrl = () => getEarlyBackendUrl("http") || sdk.getEnv()?.backendUrl || options.defaultBaseUrl;
|
|
5049
|
+
const getWsUrl = () => getEarlyBackendUrl("ws") || sdk.getEnv()?.wsBackendUrl || options.defaultWsBackendUrl;
|
|
5050
|
+
return {
|
|
5051
|
+
get baseUrl() {
|
|
5052
|
+
return getBaseUrl();
|
|
5053
|
+
},
|
|
5054
|
+
delete: (path) => sdk.network.request({
|
|
5055
|
+
method: "DELETE",
|
|
5056
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5057
|
+
}),
|
|
5058
|
+
get: (path) => sdk.network.request({
|
|
5059
|
+
headers: { "Content-Type": "application/json" },
|
|
5060
|
+
method: "GET",
|
|
5061
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5062
|
+
}),
|
|
5063
|
+
graphqlQuery: (query, variables = {}) => sdk.network.request({
|
|
5064
|
+
body: { query, variables },
|
|
5065
|
+
headers: { "Content-Type": "application/json" },
|
|
5066
|
+
method: "POST",
|
|
5067
|
+
url: buildUrl(getBaseUrl(), "/graphql")
|
|
5068
|
+
}),
|
|
5069
|
+
patch: (path, body) => sdk.network.request({
|
|
5070
|
+
body,
|
|
5071
|
+
headers: { "Content-Type": "application/json" },
|
|
5072
|
+
method: "PATCH",
|
|
5073
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5074
|
+
}),
|
|
5075
|
+
post: (path, body) => sdk.network.request({
|
|
5076
|
+
body,
|
|
5077
|
+
headers: { "Content-Type": "application/json" },
|
|
5078
|
+
method: "POST",
|
|
5079
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5080
|
+
}),
|
|
5081
|
+
get wsUrl() {
|
|
5082
|
+
return getWsUrl();
|
|
5083
|
+
}
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
|
|
5087
|
+
// src/services/wsService.ts
|
|
5088
|
+
function createWsService(options) {
|
|
5089
|
+
const channelField = options.channelField;
|
|
5090
|
+
let ws = null;
|
|
5091
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
5092
|
+
const globalListeners = /* @__PURE__ */ new Set();
|
|
5093
|
+
let reconnectTimeout = null;
|
|
5094
|
+
let reconnectDelay = 1e3;
|
|
5095
|
+
let isConnecting = false;
|
|
5096
|
+
const getWsUrl = () => options.baseUrl || options.api.wsUrl || "";
|
|
5097
|
+
const sendAction = (action, channelId) => {
|
|
5098
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
5099
|
+
ws.send(JSON.stringify({ action, [channelField]: channelId }));
|
|
5100
|
+
}
|
|
5101
|
+
};
|
|
5102
|
+
const scheduleReconnect = () => {
|
|
5103
|
+
if (reconnectTimeout) clearTimeout(reconnectTimeout);
|
|
5104
|
+
reconnectTimeout = setTimeout(() => {
|
|
5105
|
+
reconnectDelay = Math.min(reconnectDelay * 2, 3e4);
|
|
5106
|
+
connect();
|
|
5107
|
+
}, reconnectDelay);
|
|
5108
|
+
};
|
|
5109
|
+
const connect = async () => {
|
|
5110
|
+
if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING || isConnecting) {
|
|
5111
|
+
return;
|
|
5112
|
+
}
|
|
5113
|
+
isConnecting = true;
|
|
5114
|
+
try {
|
|
5115
|
+
const wsUrl = getWsUrl();
|
|
5116
|
+
if (!wsUrl) {
|
|
5117
|
+
console.warn(
|
|
5118
|
+
"[WS] No WebSocket backend URL available, connection delayed."
|
|
5119
|
+
);
|
|
5120
|
+
isConnecting = false;
|
|
5121
|
+
return;
|
|
5122
|
+
}
|
|
5123
|
+
let authParams = {};
|
|
5124
|
+
if (isRunningInsideNativeShell() && options.ticketPath !== null) {
|
|
5125
|
+
const ticketResponse = await options.api.post(
|
|
5126
|
+
options.ticketPath ?? "/ws-ticket"
|
|
5127
|
+
);
|
|
5128
|
+
authParams = { ticket: ticketResponse.data.ticket };
|
|
5129
|
+
} else if (options.devToken) {
|
|
5130
|
+
const rawJwt = options.devToken.startsWith("Bearer ") ? options.devToken.slice(7) : options.devToken;
|
|
5131
|
+
authParams = { token: rawJwt };
|
|
5132
|
+
} else if (options.requireAuth === false) {
|
|
5133
|
+
} else {
|
|
5134
|
+
console.warn("[WS] No auth available, connection delayed.");
|
|
5135
|
+
isConnecting = false;
|
|
5136
|
+
return;
|
|
5137
|
+
}
|
|
5138
|
+
const params = new URLSearchParams({ ...options.query, ...authParams });
|
|
5139
|
+
const query = params.toString();
|
|
5140
|
+
ws = new WebSocket(query ? `${wsUrl}?${query}` : wsUrl);
|
|
5141
|
+
ws.onopen = () => {
|
|
5142
|
+
isConnecting = false;
|
|
5143
|
+
reconnectDelay = 1e3;
|
|
5144
|
+
if (channelField) {
|
|
5145
|
+
for (const channelId of subscriptions.keys()) {
|
|
5146
|
+
sendAction("subscribe", channelId);
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
};
|
|
5150
|
+
ws.onmessage = (event) => {
|
|
5151
|
+
try {
|
|
5152
|
+
const payload = JSON.parse(event.data);
|
|
5153
|
+
if (channelField) {
|
|
5154
|
+
const channelId = payload[channelField];
|
|
5155
|
+
if (typeof channelId === "string") {
|
|
5156
|
+
const callbacks = subscriptions.get(channelId);
|
|
5157
|
+
if (callbacks) {
|
|
5158
|
+
for (const cb of callbacks) cb(payload);
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
for (const cb of globalListeners) cb(payload);
|
|
5163
|
+
} catch (err) {
|
|
5164
|
+
console.error("[WS] Failed to parse message:", err);
|
|
5165
|
+
}
|
|
5166
|
+
};
|
|
5167
|
+
ws.onclose = () => {
|
|
5168
|
+
isConnecting = false;
|
|
5169
|
+
scheduleReconnect();
|
|
5170
|
+
};
|
|
5171
|
+
ws.onerror = (err) => {
|
|
5172
|
+
console.error("[WS] error:", err);
|
|
5173
|
+
isConnecting = false;
|
|
5174
|
+
ws?.close();
|
|
5175
|
+
};
|
|
5176
|
+
} catch (err) {
|
|
5177
|
+
console.error("[WS] Failed to establish connection:", err);
|
|
5178
|
+
isConnecting = false;
|
|
5179
|
+
scheduleReconnect();
|
|
5180
|
+
}
|
|
5181
|
+
};
|
|
5182
|
+
const disconnect = () => {
|
|
5183
|
+
if (reconnectTimeout) {
|
|
5184
|
+
clearTimeout(reconnectTimeout);
|
|
5185
|
+
reconnectTimeout = null;
|
|
5186
|
+
}
|
|
5187
|
+
if (ws) {
|
|
5188
|
+
ws.onclose = null;
|
|
5189
|
+
ws.close();
|
|
5190
|
+
ws = null;
|
|
5191
|
+
}
|
|
5192
|
+
};
|
|
5193
|
+
const subscribe = (channelId, callback) => {
|
|
5194
|
+
const isNewChannel = !subscriptions.has(channelId);
|
|
5195
|
+
if (isNewChannel) subscriptions.set(channelId, /* @__PURE__ */ new Set());
|
|
5196
|
+
subscriptions.get(channelId)?.add(callback);
|
|
5197
|
+
if (isNewChannel) sendAction("subscribe", channelId);
|
|
5198
|
+
connect();
|
|
5199
|
+
return () => {
|
|
5200
|
+
const set = subscriptions.get(channelId);
|
|
5201
|
+
if (!set) return;
|
|
5202
|
+
set.delete(callback);
|
|
5203
|
+
if (set.size === 0) {
|
|
5204
|
+
subscriptions.delete(channelId);
|
|
5205
|
+
sendAction("unsubscribe", channelId);
|
|
5206
|
+
}
|
|
5207
|
+
};
|
|
5208
|
+
};
|
|
5209
|
+
const onMessage = (callback) => {
|
|
5210
|
+
globalListeners.add(callback);
|
|
5211
|
+
connect();
|
|
5212
|
+
return () => {
|
|
5213
|
+
globalListeners.delete(callback);
|
|
5214
|
+
};
|
|
5215
|
+
};
|
|
5216
|
+
if (channelField) {
|
|
5217
|
+
return { connect, disconnect, onMessage, subscribe };
|
|
5218
|
+
}
|
|
5219
|
+
return { connect, disconnect, onMessage };
|
|
5220
|
+
}
|
|
5221
|
+
|
|
4970
5222
|
// src/index.ts
|
|
4971
5223
|
var src_default = sdk;
|
|
4972
5224
|
export {
|
|
@@ -4990,6 +5242,8 @@ export {
|
|
|
4990
5242
|
SDKProvider,
|
|
4991
5243
|
SwipeCarousel,
|
|
4992
5244
|
UIEvent,
|
|
5245
|
+
createApiService,
|
|
5246
|
+
createWsService,
|
|
4993
5247
|
src_default as default,
|
|
4994
5248
|
handleClearRoute,
|
|
4995
5249
|
handleDrawRoute,
|