@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.cjs
CHANGED
|
@@ -50,6 +50,8 @@ __export(src_exports, {
|
|
|
50
50
|
SDKProvider: () => SDKProvider,
|
|
51
51
|
SwipeCarousel: () => SwipeCarousel,
|
|
52
52
|
UIEvent: () => UIEvent,
|
|
53
|
+
createApiService: () => createApiService,
|
|
54
|
+
createWsService: () => createWsService,
|
|
53
55
|
default: () => src_default,
|
|
54
56
|
handleClearRoute: () => handleClearRoute,
|
|
55
57
|
handleDrawRoute: () => handleDrawRoute,
|
|
@@ -124,7 +126,16 @@ var Button = React.forwardRef(
|
|
|
124
126
|
);
|
|
125
127
|
Button.displayName = "Button";
|
|
126
128
|
|
|
129
|
+
// src/utils/environment.ts
|
|
130
|
+
function isRunningInsideNativeShell() {
|
|
131
|
+
if (typeof window === "undefined") return false;
|
|
132
|
+
const w = window;
|
|
133
|
+
return w.__UP_REACT_NATIVE_APP__ === true || !!w.ReactNativeWebView;
|
|
134
|
+
}
|
|
135
|
+
|
|
127
136
|
// src/bridge.ts
|
|
137
|
+
var BridgeTimeoutError = class extends Error {
|
|
138
|
+
};
|
|
128
139
|
var Bridge = class {
|
|
129
140
|
callbacks = /* @__PURE__ */ new Map();
|
|
130
141
|
eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -195,18 +206,32 @@ var Bridge = class {
|
|
|
195
206
|
* @param type The action type (e.g., "GET_LOCATION").
|
|
196
207
|
* @param payload Data associated with the action.
|
|
197
208
|
* @param timeoutMs Time in milliseconds to wait for a response before rejecting.
|
|
209
|
+
* @param retries Number of additional attempts if a request times out (default 1 — one retry).
|
|
210
|
+
* Exists because the response for a request can be lost in transit — e.g. the host's
|
|
211
|
+
* injected response script running before this WebView's own `message` listener has
|
|
212
|
+
* attached, worse under rapid mount/unmount cycles — with no application-visible symptom
|
|
213
|
+
* other than the full 30s timeout. A blind retry self-heals that class of loss without
|
|
214
|
+
* needing to diagnose which specific transport race dropped the original message; every
|
|
215
|
+
* call site benefits automatically instead of each consumer app hand-rolling its own
|
|
216
|
+
* retry-after-catch logic (which previously only covered `initialize()`, and imperfectly).
|
|
198
217
|
* @returns A promise that resolves with the native response.
|
|
199
218
|
*/
|
|
200
|
-
send(type, payload, timeoutMs = 3e4) {
|
|
219
|
+
send(type, payload, timeoutMs = 3e4, retries = 1) {
|
|
201
220
|
if (!this.isNative()) {
|
|
202
221
|
return this.handleMockMessage(type, payload);
|
|
203
222
|
}
|
|
223
|
+
return this.sendOnce(type, payload, timeoutMs).catch((err) => {
|
|
224
|
+
if (retries <= 0 || !(err instanceof BridgeTimeoutError)) throw err;
|
|
225
|
+
return this.send(type, payload, timeoutMs, retries - 1);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
sendOnce(type, payload, timeoutMs) {
|
|
204
229
|
return new Promise((resolve, reject) => {
|
|
205
230
|
const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
206
231
|
const timeout = setTimeout(() => {
|
|
207
232
|
if (this.callbacks.has(callbackId)) {
|
|
208
233
|
this.callbacks.delete(callbackId);
|
|
209
|
-
reject(new
|
|
234
|
+
reject(new BridgeTimeoutError(`Bridge request timeout: ${type}`));
|
|
210
235
|
}
|
|
211
236
|
}, timeoutMs);
|
|
212
237
|
this.callbacks.set(callbackId, {
|
|
@@ -280,8 +305,7 @@ var Bridge = class {
|
|
|
280
305
|
* Determine environment. Check flag or cache result.
|
|
281
306
|
*/
|
|
282
307
|
isNative() {
|
|
283
|
-
|
|
284
|
-
return window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView;
|
|
308
|
+
return isRunningInsideNativeShell();
|
|
285
309
|
}
|
|
286
310
|
};
|
|
287
311
|
|
|
@@ -2028,14 +2052,14 @@ var useUserLocation = () => {
|
|
|
2028
2052
|
});
|
|
2029
2053
|
const mapPayload = (0, import_react6.useCallback)((payload) => {
|
|
2030
2054
|
const coords = payload.location?.coords || payload.coords;
|
|
2031
|
-
const headingData = payload.heading
|
|
2055
|
+
const headingData = payload.heading;
|
|
2032
2056
|
const newCoords = coords ? [coords.longitude, coords.latitude] : null;
|
|
2033
2057
|
let heading = null;
|
|
2034
2058
|
if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
|
|
2035
2059
|
heading = headingData.trueHeading;
|
|
2036
2060
|
} else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
|
|
2037
2061
|
heading = headingData.magneticHeading;
|
|
2038
|
-
} else if (headingData?.heading !== null && headingData
|
|
2062
|
+
} else if (headingData?.heading !== null && headingData?.heading >= 0) {
|
|
2039
2063
|
heading = headingData.heading;
|
|
2040
2064
|
}
|
|
2041
2065
|
return {
|
|
@@ -2505,7 +2529,14 @@ var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
|
2505
2529
|
var MockRadiusLayer = ({
|
|
2506
2530
|
center,
|
|
2507
2531
|
radiusMeters,
|
|
2508
|
-
color
|
|
2532
|
+
// Neutral gray, not any one app's accent color — this debug aid is shared
|
|
2533
|
+
// across every mini-app (each with its own, different palette), so it
|
|
2534
|
+
// shouldn't default to a color that reads as "part of the app" (was
|
|
2535
|
+
// #39ff88, a neon green/yellow left over from an earlier shared theme,
|
|
2536
|
+
// reported as looking like an unwanted yellow border around the user's
|
|
2537
|
+
// position marker in up-miniapps-explore, which has since moved to a
|
|
2538
|
+
// pastel palette with no neon accents at all).
|
|
2539
|
+
color = "#e2e8f0"
|
|
2509
2540
|
}) => {
|
|
2510
2541
|
const data = (0, import_react10.useMemo)(
|
|
2511
2542
|
() => buildGeoCircle(center, radiusMeters),
|
|
@@ -3680,7 +3711,7 @@ var RoutingService = {
|
|
|
3680
3711
|
};
|
|
3681
3712
|
|
|
3682
3713
|
// package.json
|
|
3683
|
-
var version = "0.
|
|
3714
|
+
var version = "0.6.1";
|
|
3684
3715
|
|
|
3685
3716
|
// src/sdk.ts
|
|
3686
3717
|
function deepFreeze(obj) {
|
|
@@ -3695,6 +3726,7 @@ function deepFreeze(obj) {
|
|
|
3695
3726
|
return Object.freeze(obj);
|
|
3696
3727
|
}
|
|
3697
3728
|
var globalRoutingConfig = null;
|
|
3729
|
+
var lastKnownEnv = null;
|
|
3698
3730
|
var MiniAppSDK = class {
|
|
3699
3731
|
/**
|
|
3700
3732
|
* Map services.
|
|
@@ -4017,7 +4049,14 @@ var MiniAppSDK = class {
|
|
|
4017
4049
|
});
|
|
4018
4050
|
constructor() {
|
|
4019
4051
|
this.bridge = new Bridge();
|
|
4052
|
+
this.bridge.on(
|
|
4053
|
+
"UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
|
|
4054
|
+
(data) => {
|
|
4055
|
+
if (data?.env) lastKnownEnv = data.env;
|
|
4056
|
+
}
|
|
4057
|
+
);
|
|
4020
4058
|
this.initialize = this.initialize.bind(this);
|
|
4059
|
+
this.getEnv = this.getEnv.bind(this);
|
|
4021
4060
|
this.on = this.on.bind(this);
|
|
4022
4061
|
this.off = this.off.bind(this);
|
|
4023
4062
|
}
|
|
@@ -4041,8 +4080,22 @@ var MiniAppSDK = class {
|
|
|
4041
4080
|
* The starting point for the Mini-App. Must be called before any other SDK methods.
|
|
4042
4081
|
* @param options The options to provide to the shell app
|
|
4043
4082
|
*/
|
|
4044
|
-
initialize(options) {
|
|
4045
|
-
|
|
4083
|
+
async initialize(options) {
|
|
4084
|
+
const res = await this.bridge.send(
|
|
4085
|
+
"INITIALIZE",
|
|
4086
|
+
options
|
|
4087
|
+
);
|
|
4088
|
+
if (res?.env) lastKnownEnv = res.env;
|
|
4089
|
+
return res;
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* Returns the last known `env` (shell-provided config like `backendUrl`),
|
|
4093
|
+
* refreshed automatically by `initialize()` and every `UPDATE_CONTEXT`
|
|
4094
|
+
* push — read this instead of caching your own copy of `env` values.
|
|
4095
|
+
* Returns `null` before the first successful `initialize()`/context push.
|
|
4096
|
+
*/
|
|
4097
|
+
getEnv() {
|
|
4098
|
+
return lastKnownEnv;
|
|
4046
4099
|
}
|
|
4047
4100
|
/**
|
|
4048
4101
|
* Registers a callback for a specific event from the native shell.
|
|
@@ -5249,6 +5302,207 @@ function handleClearRoute(map, miniAppId) {
|
|
|
5249
5302
|
if (map.getSource(sourceId)) map.removeSource(sourceId);
|
|
5250
5303
|
}
|
|
5251
5304
|
|
|
5305
|
+
// src/services/apiService.ts
|
|
5306
|
+
function buildUrl(baseUrl, path) {
|
|
5307
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
5308
|
+
return path;
|
|
5309
|
+
}
|
|
5310
|
+
let cleanBase = baseUrl.trim();
|
|
5311
|
+
while (cleanBase.endsWith("/")) {
|
|
5312
|
+
cleanBase = cleanBase.slice(0, -1);
|
|
5313
|
+
}
|
|
5314
|
+
let cleanPath = path.trim();
|
|
5315
|
+
if (!cleanPath.startsWith("/")) {
|
|
5316
|
+
cleanPath = `/${cleanPath}`;
|
|
5317
|
+
}
|
|
5318
|
+
const baseEndsWithV1 = cleanBase.endsWith("/api/v1");
|
|
5319
|
+
const pathStartsWithV1 = cleanPath.startsWith("/api/v1/") || cleanPath === "/api/v1";
|
|
5320
|
+
if (baseEndsWithV1 && pathStartsWithV1) {
|
|
5321
|
+
const strippedPath = cleanPath.slice(7);
|
|
5322
|
+
return `${cleanBase}${strippedPath}`;
|
|
5323
|
+
}
|
|
5324
|
+
return `${cleanBase}${cleanPath}`;
|
|
5325
|
+
}
|
|
5326
|
+
function getEarlyBackendUrl(kind) {
|
|
5327
|
+
if (typeof window === "undefined") return void 0;
|
|
5328
|
+
const early = window.__UP_BACKEND_URLS__;
|
|
5329
|
+
return early?.[kind] ?? void 0;
|
|
5330
|
+
}
|
|
5331
|
+
function createApiService(options) {
|
|
5332
|
+
const getBaseUrl = () => getEarlyBackendUrl("http") || sdk.getEnv()?.backendUrl || options.defaultBaseUrl;
|
|
5333
|
+
const getWsUrl = () => getEarlyBackendUrl("ws") || sdk.getEnv()?.wsBackendUrl || options.defaultWsBackendUrl;
|
|
5334
|
+
return {
|
|
5335
|
+
get baseUrl() {
|
|
5336
|
+
return getBaseUrl();
|
|
5337
|
+
},
|
|
5338
|
+
delete: (path) => sdk.network.request({
|
|
5339
|
+
method: "DELETE",
|
|
5340
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5341
|
+
}),
|
|
5342
|
+
get: (path) => sdk.network.request({
|
|
5343
|
+
headers: { "Content-Type": "application/json" },
|
|
5344
|
+
method: "GET",
|
|
5345
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5346
|
+
}),
|
|
5347
|
+
graphqlQuery: (query, variables = {}) => sdk.network.request({
|
|
5348
|
+
body: { query, variables },
|
|
5349
|
+
headers: { "Content-Type": "application/json" },
|
|
5350
|
+
method: "POST",
|
|
5351
|
+
url: buildUrl(getBaseUrl(), "/graphql")
|
|
5352
|
+
}),
|
|
5353
|
+
patch: (path, body) => sdk.network.request({
|
|
5354
|
+
body,
|
|
5355
|
+
headers: { "Content-Type": "application/json" },
|
|
5356
|
+
method: "PATCH",
|
|
5357
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5358
|
+
}),
|
|
5359
|
+
post: (path, body) => sdk.network.request({
|
|
5360
|
+
body,
|
|
5361
|
+
headers: { "Content-Type": "application/json" },
|
|
5362
|
+
method: "POST",
|
|
5363
|
+
url: buildUrl(getBaseUrl(), path)
|
|
5364
|
+
}),
|
|
5365
|
+
get wsUrl() {
|
|
5366
|
+
return getWsUrl();
|
|
5367
|
+
}
|
|
5368
|
+
};
|
|
5369
|
+
}
|
|
5370
|
+
|
|
5371
|
+
// src/services/wsService.ts
|
|
5372
|
+
function createWsService(options) {
|
|
5373
|
+
const channelField = options.channelField;
|
|
5374
|
+
let ws = null;
|
|
5375
|
+
const subscriptions = /* @__PURE__ */ new Map();
|
|
5376
|
+
const globalListeners = /* @__PURE__ */ new Set();
|
|
5377
|
+
let reconnectTimeout = null;
|
|
5378
|
+
let reconnectDelay = 1e3;
|
|
5379
|
+
let isConnecting = false;
|
|
5380
|
+
const getWsUrl = () => options.baseUrl || options.api.wsUrl || "";
|
|
5381
|
+
const sendAction = (action, channelId) => {
|
|
5382
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
5383
|
+
ws.send(JSON.stringify({ action, [channelField]: channelId }));
|
|
5384
|
+
}
|
|
5385
|
+
};
|
|
5386
|
+
const scheduleReconnect = () => {
|
|
5387
|
+
if (reconnectTimeout) clearTimeout(reconnectTimeout);
|
|
5388
|
+
reconnectTimeout = setTimeout(() => {
|
|
5389
|
+
reconnectDelay = Math.min(reconnectDelay * 2, 3e4);
|
|
5390
|
+
connect();
|
|
5391
|
+
}, reconnectDelay);
|
|
5392
|
+
};
|
|
5393
|
+
const connect = async () => {
|
|
5394
|
+
if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING || isConnecting) {
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5397
|
+
isConnecting = true;
|
|
5398
|
+
try {
|
|
5399
|
+
const wsUrl = getWsUrl();
|
|
5400
|
+
if (!wsUrl) {
|
|
5401
|
+
console.warn(
|
|
5402
|
+
"[WS] No WebSocket backend URL available, connection delayed."
|
|
5403
|
+
);
|
|
5404
|
+
isConnecting = false;
|
|
5405
|
+
return;
|
|
5406
|
+
}
|
|
5407
|
+
let authParams = {};
|
|
5408
|
+
if (isRunningInsideNativeShell() && options.ticketPath !== null) {
|
|
5409
|
+
const ticketResponse = await options.api.post(
|
|
5410
|
+
options.ticketPath ?? "/ws-ticket"
|
|
5411
|
+
);
|
|
5412
|
+
authParams = { ticket: ticketResponse.data.ticket };
|
|
5413
|
+
} else if (options.devToken) {
|
|
5414
|
+
const rawJwt = options.devToken.startsWith("Bearer ") ? options.devToken.slice(7) : options.devToken;
|
|
5415
|
+
authParams = { token: rawJwt };
|
|
5416
|
+
} else if (options.requireAuth === false) {
|
|
5417
|
+
} else {
|
|
5418
|
+
console.warn("[WS] No auth available, connection delayed.");
|
|
5419
|
+
isConnecting = false;
|
|
5420
|
+
return;
|
|
5421
|
+
}
|
|
5422
|
+
const params = new URLSearchParams({ ...options.query, ...authParams });
|
|
5423
|
+
const query = params.toString();
|
|
5424
|
+
ws = new WebSocket(query ? `${wsUrl}?${query}` : wsUrl);
|
|
5425
|
+
ws.onopen = () => {
|
|
5426
|
+
isConnecting = false;
|
|
5427
|
+
reconnectDelay = 1e3;
|
|
5428
|
+
if (channelField) {
|
|
5429
|
+
for (const channelId of subscriptions.keys()) {
|
|
5430
|
+
sendAction("subscribe", channelId);
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
};
|
|
5434
|
+
ws.onmessage = (event) => {
|
|
5435
|
+
try {
|
|
5436
|
+
const payload = JSON.parse(event.data);
|
|
5437
|
+
if (channelField) {
|
|
5438
|
+
const channelId = payload[channelField];
|
|
5439
|
+
if (typeof channelId === "string") {
|
|
5440
|
+
const callbacks = subscriptions.get(channelId);
|
|
5441
|
+
if (callbacks) {
|
|
5442
|
+
for (const cb of callbacks) cb(payload);
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
for (const cb of globalListeners) cb(payload);
|
|
5447
|
+
} catch (err) {
|
|
5448
|
+
console.error("[WS] Failed to parse message:", err);
|
|
5449
|
+
}
|
|
5450
|
+
};
|
|
5451
|
+
ws.onclose = () => {
|
|
5452
|
+
isConnecting = false;
|
|
5453
|
+
scheduleReconnect();
|
|
5454
|
+
};
|
|
5455
|
+
ws.onerror = (err) => {
|
|
5456
|
+
console.error("[WS] error:", err);
|
|
5457
|
+
isConnecting = false;
|
|
5458
|
+
ws?.close();
|
|
5459
|
+
};
|
|
5460
|
+
} catch (err) {
|
|
5461
|
+
console.error("[WS] Failed to establish connection:", err);
|
|
5462
|
+
isConnecting = false;
|
|
5463
|
+
scheduleReconnect();
|
|
5464
|
+
}
|
|
5465
|
+
};
|
|
5466
|
+
const disconnect = () => {
|
|
5467
|
+
if (reconnectTimeout) {
|
|
5468
|
+
clearTimeout(reconnectTimeout);
|
|
5469
|
+
reconnectTimeout = null;
|
|
5470
|
+
}
|
|
5471
|
+
if (ws) {
|
|
5472
|
+
ws.onclose = null;
|
|
5473
|
+
ws.close();
|
|
5474
|
+
ws = null;
|
|
5475
|
+
}
|
|
5476
|
+
};
|
|
5477
|
+
const subscribe = (channelId, callback) => {
|
|
5478
|
+
const isNewChannel = !subscriptions.has(channelId);
|
|
5479
|
+
if (isNewChannel) subscriptions.set(channelId, /* @__PURE__ */ new Set());
|
|
5480
|
+
subscriptions.get(channelId)?.add(callback);
|
|
5481
|
+
if (isNewChannel) sendAction("subscribe", channelId);
|
|
5482
|
+
connect();
|
|
5483
|
+
return () => {
|
|
5484
|
+
const set = subscriptions.get(channelId);
|
|
5485
|
+
if (!set) return;
|
|
5486
|
+
set.delete(callback);
|
|
5487
|
+
if (set.size === 0) {
|
|
5488
|
+
subscriptions.delete(channelId);
|
|
5489
|
+
sendAction("unsubscribe", channelId);
|
|
5490
|
+
}
|
|
5491
|
+
};
|
|
5492
|
+
};
|
|
5493
|
+
const onMessage = (callback) => {
|
|
5494
|
+
globalListeners.add(callback);
|
|
5495
|
+
connect();
|
|
5496
|
+
return () => {
|
|
5497
|
+
globalListeners.delete(callback);
|
|
5498
|
+
};
|
|
5499
|
+
};
|
|
5500
|
+
if (channelField) {
|
|
5501
|
+
return { connect, disconnect, onMessage, subscribe };
|
|
5502
|
+
}
|
|
5503
|
+
return { connect, disconnect, onMessage };
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5252
5506
|
// src/index.ts
|
|
5253
5507
|
var src_default = sdk;
|
|
5254
5508
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -5273,6 +5527,8 @@ var src_default = sdk;
|
|
|
5273
5527
|
SDKProvider,
|
|
5274
5528
|
SwipeCarousel,
|
|
5275
5529
|
UIEvent,
|
|
5530
|
+
createApiService,
|
|
5531
|
+
createWsService,
|
|
5276
5532
|
handleClearRoute,
|
|
5277
5533
|
handleDrawRoute,
|
|
5278
5534
|
handleRouteRequest,
|