@cloudparse/up-miniapps-sdk 0.6.0 → 0.6.2

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/dist/index.cjs CHANGED
@@ -51,6 +51,7 @@ __export(src_exports, {
51
51
  SwipeCarousel: () => SwipeCarousel,
52
52
  UIEvent: () => UIEvent,
53
53
  createApiService: () => createApiService,
54
+ createWsService: () => createWsService,
54
55
  default: () => src_default,
55
56
  handleClearRoute: () => handleClearRoute,
56
57
  handleDrawRoute: () => handleDrawRoute,
@@ -125,6 +126,13 @@ var Button = React.forwardRef(
125
126
  );
126
127
  Button.displayName = "Button";
127
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
+
128
136
  // src/bridge.ts
129
137
  var BridgeTimeoutError = class extends Error {
130
138
  };
@@ -297,8 +305,7 @@ var Bridge = class {
297
305
  * Determine environment. Check flag or cache result.
298
306
  */
299
307
  isNative() {
300
- if (typeof window === "undefined") return false;
301
- return window.__UP_REACT_NATIVE_APP__ === true || !!window.ReactNativeWebView;
308
+ return isRunningInsideNativeShell();
302
309
  }
303
310
  };
304
311
 
@@ -2045,14 +2052,14 @@ var useUserLocation = () => {
2045
2052
  });
2046
2053
  const mapPayload = (0, import_react6.useCallback)((payload) => {
2047
2054
  const coords = payload.location?.coords || payload.coords;
2048
- const headingData = payload.heading || payload.coords;
2055
+ const headingData = payload.heading;
2049
2056
  const newCoords = coords ? [coords.longitude, coords.latitude] : null;
2050
2057
  let heading = null;
2051
2058
  if (headingData?.trueHeading !== void 0 && headingData.trueHeading >= 0) {
2052
2059
  heading = headingData.trueHeading;
2053
2060
  } else if (headingData?.magneticHeading !== void 0 && headingData.magneticHeading >= 0) {
2054
2061
  heading = headingData.magneticHeading;
2055
- } else if (headingData?.heading !== null && headingData.heading >= 0) {
2062
+ } else if (headingData?.heading !== null && headingData?.heading >= 0) {
2056
2063
  heading = headingData.heading;
2057
2064
  }
2058
2065
  return {
@@ -2522,7 +2529,14 @@ var import_jsx_runtime11 = require("react/jsx-runtime");
2522
2529
  var MockRadiusLayer = ({
2523
2530
  center,
2524
2531
  radiusMeters,
2525
- color = "#39ff88"
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"
2526
2540
  }) => {
2527
2541
  const data = (0, import_react10.useMemo)(
2528
2542
  () => buildGeoCircle(center, radiusMeters),
@@ -3697,7 +3711,7 @@ var RoutingService = {
3697
3711
  };
3698
3712
 
3699
3713
  // package.json
3700
- var version = "0.6.0";
3714
+ var version = "0.6.2";
3701
3715
 
3702
3716
  // src/sdk.ts
3703
3717
  function deepFreeze(obj) {
@@ -5354,6 +5368,163 @@ function createApiService(options) {
5354
5368
  };
5355
5369
  }
5356
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
+ let status = "closed";
5381
+ const statusListeners = /* @__PURE__ */ new Set();
5382
+ const setStatus = (next) => {
5383
+ if (status === next) return;
5384
+ status = next;
5385
+ for (const cb of statusListeners) cb(status);
5386
+ };
5387
+ const getWsUrl = () => options.baseUrl || options.api.wsUrl || "";
5388
+ const sendAction = (action, channelId) => {
5389
+ if (ws?.readyState === WebSocket.OPEN) {
5390
+ ws.send(JSON.stringify({ action, [channelField]: channelId }));
5391
+ }
5392
+ };
5393
+ const scheduleReconnect = () => {
5394
+ if (reconnectTimeout) clearTimeout(reconnectTimeout);
5395
+ reconnectTimeout = setTimeout(() => {
5396
+ reconnectDelay = Math.min(reconnectDelay * 2, 3e4);
5397
+ connect();
5398
+ }, reconnectDelay);
5399
+ };
5400
+ const connect = async () => {
5401
+ if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING || isConnecting) {
5402
+ return;
5403
+ }
5404
+ isConnecting = true;
5405
+ setStatus("connecting");
5406
+ try {
5407
+ const wsUrl = getWsUrl();
5408
+ if (!wsUrl) {
5409
+ console.warn(
5410
+ "[WS] No WebSocket backend URL available, connection delayed."
5411
+ );
5412
+ isConnecting = false;
5413
+ setStatus("closed");
5414
+ return;
5415
+ }
5416
+ let authParams = {};
5417
+ if (isRunningInsideNativeShell() && options.ticketPath !== null) {
5418
+ const ticketResponse = await options.api.post(
5419
+ options.ticketPath ?? "/ws-ticket"
5420
+ );
5421
+ authParams = { ticket: ticketResponse.data.ticket };
5422
+ } else if (options.devToken) {
5423
+ const rawJwt = options.devToken.startsWith("Bearer ") ? options.devToken.slice(7) : options.devToken;
5424
+ authParams = { token: rawJwt };
5425
+ } else if (options.requireAuth === false) {
5426
+ } else {
5427
+ console.warn("[WS] No auth available, connection delayed.");
5428
+ isConnecting = false;
5429
+ setStatus("closed");
5430
+ return;
5431
+ }
5432
+ const params = new URLSearchParams({ ...options.query, ...authParams });
5433
+ const query = params.toString();
5434
+ ws = new WebSocket(query ? `${wsUrl}?${query}` : wsUrl);
5435
+ ws.onopen = () => {
5436
+ isConnecting = false;
5437
+ setStatus("open");
5438
+ reconnectDelay = 1e3;
5439
+ if (channelField) {
5440
+ for (const channelId of subscriptions.keys()) {
5441
+ sendAction("subscribe", channelId);
5442
+ }
5443
+ }
5444
+ };
5445
+ ws.onmessage = (event) => {
5446
+ try {
5447
+ const payload = JSON.parse(event.data);
5448
+ if (channelField) {
5449
+ const channelId = payload[channelField];
5450
+ if (typeof channelId === "string") {
5451
+ const callbacks = subscriptions.get(channelId);
5452
+ if (callbacks) {
5453
+ for (const cb of callbacks) cb(payload);
5454
+ }
5455
+ }
5456
+ }
5457
+ for (const cb of globalListeners) cb(payload);
5458
+ } catch (err) {
5459
+ console.error("[WS] Failed to parse message:", err);
5460
+ }
5461
+ };
5462
+ ws.onclose = () => {
5463
+ isConnecting = false;
5464
+ setStatus("closed");
5465
+ scheduleReconnect();
5466
+ };
5467
+ ws.onerror = (err) => {
5468
+ console.error("[WS] error:", err);
5469
+ isConnecting = false;
5470
+ ws?.close();
5471
+ };
5472
+ } catch (err) {
5473
+ console.error("[WS] Failed to establish connection:", err);
5474
+ isConnecting = false;
5475
+ setStatus("closed");
5476
+ scheduleReconnect();
5477
+ }
5478
+ };
5479
+ const disconnect = () => {
5480
+ if (reconnectTimeout) {
5481
+ clearTimeout(reconnectTimeout);
5482
+ reconnectTimeout = null;
5483
+ }
5484
+ if (ws) {
5485
+ ws.onclose = null;
5486
+ ws.close();
5487
+ ws = null;
5488
+ }
5489
+ setStatus("closed");
5490
+ };
5491
+ const getStatus = () => status;
5492
+ const onStatusChange = (callback) => {
5493
+ statusListeners.add(callback);
5494
+ callback(status);
5495
+ return () => {
5496
+ statusListeners.delete(callback);
5497
+ };
5498
+ };
5499
+ const subscribe = (channelId, callback) => {
5500
+ const isNewChannel = !subscriptions.has(channelId);
5501
+ if (isNewChannel) subscriptions.set(channelId, /* @__PURE__ */ new Set());
5502
+ subscriptions.get(channelId)?.add(callback);
5503
+ if (isNewChannel) sendAction("subscribe", channelId);
5504
+ connect();
5505
+ return () => {
5506
+ const set = subscriptions.get(channelId);
5507
+ if (!set) return;
5508
+ set.delete(callback);
5509
+ if (set.size === 0) {
5510
+ subscriptions.delete(channelId);
5511
+ sendAction("unsubscribe", channelId);
5512
+ }
5513
+ };
5514
+ };
5515
+ const onMessage = (callback) => {
5516
+ globalListeners.add(callback);
5517
+ connect();
5518
+ return () => {
5519
+ globalListeners.delete(callback);
5520
+ };
5521
+ };
5522
+ if (channelField) {
5523
+ return { connect, disconnect, onMessage, subscribe, getStatus, onStatusChange };
5524
+ }
5525
+ return { connect, disconnect, onMessage, getStatus, onStatusChange };
5526
+ }
5527
+
5357
5528
  // src/index.ts
5358
5529
  var src_default = sdk;
5359
5530
  // Annotate the CommonJS export names for ESM import in node:
@@ -5379,6 +5550,7 @@ var src_default = sdk;
5379
5550
  SwipeCarousel,
5380
5551
  UIEvent,
5381
5552
  createApiService,
5553
+ createWsService,
5382
5554
  handleClearRoute,
5383
5555
  handleDrawRoute,
5384
5556
  handleRouteRequest,