@cloudparse/up-miniapps-sdk 0.6.1 → 0.6.3

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
@@ -225,35 +225,6 @@ var Bridge = class {
225
225
  return this.send(type, payload, timeoutMs, retries - 1);
226
226
  });
227
227
  }
228
- sendOnce(type, payload, timeoutMs) {
229
- return new Promise((resolve, reject) => {
230
- const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
231
- const timeout = setTimeout(() => {
232
- if (this.callbacks.has(callbackId)) {
233
- this.callbacks.delete(callbackId);
234
- reject(new BridgeTimeoutError(`Bridge request timeout: ${type}`));
235
- }
236
- }, timeoutMs);
237
- this.callbacks.set(callbackId, {
238
- reject,
239
- resolve,
240
- timeout
241
- });
242
- const message = {
243
- callbackId,
244
- payload,
245
- type
246
- };
247
- const win = window;
248
- if (win.ReactNativeWebView?.postMessage) {
249
- win.ReactNativeWebView.postMessage(JSON.stringify(message));
250
- } else {
251
- clearTimeout(timeout);
252
- this.callbacks.delete(callbackId);
253
- reject(new Error("Bridge not available"));
254
- }
255
- });
256
- }
257
228
  /**
258
229
  * Internal handler for all incoming messages from the native shell.
259
230
  * Matches incoming payloads to either pending callbacks or event listeners.
@@ -301,6 +272,35 @@ var Bridge = class {
301
272
  console.warn(`[Bridge] Mock mode active: Unhandled message type ${type}`);
302
273
  return Promise.resolve();
303
274
  }
275
+ sendOnce(type, payload, timeoutMs) {
276
+ return new Promise((resolve, reject) => {
277
+ const callbackId = `${type}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
278
+ const timeout = setTimeout(() => {
279
+ if (this.callbacks.has(callbackId)) {
280
+ this.callbacks.delete(callbackId);
281
+ reject(new BridgeTimeoutError(`Bridge request timeout: ${type}`));
282
+ }
283
+ }, timeoutMs);
284
+ this.callbacks.set(callbackId, {
285
+ reject,
286
+ resolve,
287
+ timeout
288
+ });
289
+ const message = {
290
+ callbackId,
291
+ payload,
292
+ type
293
+ };
294
+ const win = window;
295
+ if (win.ReactNativeWebView?.postMessage) {
296
+ win.ReactNativeWebView.postMessage(JSON.stringify(message));
297
+ } else {
298
+ clearTimeout(timeout);
299
+ this.callbacks.delete(callbackId);
300
+ reject(new Error("Bridge not available"));
301
+ }
302
+ });
303
+ }
304
304
  /**
305
305
  * Determine environment. Check flag or cache result.
306
306
  */
@@ -2495,11 +2495,6 @@ var RouteLayer = ({
2495
2495
  ] });
2496
2496
  };
2497
2497
 
2498
- // src/components/Map/Map.tsx
2499
- var import_react_maplibre6 = require("@vis.gl/react-maplibre");
2500
- var import_react12 = __toESM(require("react"), 1);
2501
- var import_maplibre_gl = require("maplibre-gl/dist/maplibre-gl.css");
2502
-
2503
2498
  // src/components/Map/utils/geoCircle.ts
2504
2499
  var EARTH_RADIUS_METERS = 6371e3;
2505
2500
  var buildGeoCircle = (center, radiusMeters, points = 64) => {
@@ -2891,6 +2886,9 @@ var UserLocationLayer = ({
2891
2886
  };
2892
2887
 
2893
2888
  // src/components/Map/Map.tsx
2889
+ var import_react_maplibre6 = require("@vis.gl/react-maplibre");
2890
+ var import_react12 = __toESM(require("react"), 1);
2891
+ var import_maplibre_gl = require("maplibre-gl/dist/maplibre-gl.css");
2894
2892
  var import_jsx_runtime15 = require("react/jsx-runtime");
2895
2893
  var MapInternal = ({
2896
2894
  initialViewState = {
@@ -3426,7 +3424,12 @@ async function generateQrCode(data, options = {}) {
3426
3424
  const outerPadding = options.outerPadding ?? 32;
3427
3425
  const borderRadiusPercent = options.borderRadius ?? DEFAULT_BORDER_RADIUS_PERCENT;
3428
3426
  const composed = label ? await composeWithLabel(qrDataUrl, label, size, backgroundColor, color) : qrDataUrl;
3429
- return finalizeImage(composed, outerPadding, borderRadiusPercent, backgroundColor);
3427
+ return finalizeImage(
3428
+ composed,
3429
+ outerPadding,
3430
+ borderRadiusPercent,
3431
+ backgroundColor
3432
+ );
3430
3433
  }
3431
3434
  var blobToDataUrl = (blob) => new Promise((resolve, reject) => {
3432
3435
  const reader = new FileReader();
@@ -3711,7 +3714,7 @@ var RoutingService = {
3711
3714
  };
3712
3715
 
3713
3716
  // package.json
3714
- var version = "0.6.1";
3717
+ var version = "0.6.3";
3715
3718
 
3716
3719
  // src/sdk.ts
3717
3720
  function deepFreeze(obj) {
@@ -3739,6 +3742,28 @@ var MiniAppSDK = class {
3739
3742
  */
3740
3743
  render: renderMap
3741
3744
  });
3745
+ /**
3746
+ * QR code generation — the single source of truth for every Mini-App.
3747
+ * Runs entirely client-side (canvas, no Host Shell round-trip) and returns
3748
+ * a base64 PNG data URI, ready for an `<img src>` or `media.saveImage()`.
3749
+ * Mini-Apps must not reimplement this on their own.
3750
+ */
3751
+ qrCode = Object.freeze({
3752
+ /**
3753
+ * Decodes a QR code from a static image (base64 data URI or URL) —
3754
+ * e.g. a photo picked via `sdk.media.pickImage()` that already
3755
+ * contains a QR code, as an alternative to the live `<QRScanner>`.
3756
+ * @param imageDataUrl A base64 data URI (e.g. from `sdk.media.pickImage()`).
3757
+ * @returns The decoded string, or `null` if no QR code was found.
3758
+ */
3759
+ decode: (imageDataUrl) => decodeQrCode(imageDataUrl),
3760
+ /**
3761
+ * @param data The value to encode (e.g. a deep link).
3762
+ * @param options Styling/labeling options — see {@link IGenerateQrCodeOptions}.
3763
+ * @returns A base64 PNG data URI.
3764
+ */
3765
+ generate: (data, options) => generateQrCode(data, options)
3766
+ });
3742
3767
  bridge;
3743
3768
  /**
3744
3769
  * Local SQLite Storage.
@@ -3842,28 +3867,6 @@ var MiniAppSDK = class {
3842
3867
  */
3843
3868
  saveImage: (base64, filename) => this.bridge.send("SAVE_IMAGE", { base64, filename })
3844
3869
  });
3845
- /**
3846
- * QR code generation — the single source of truth for every Mini-App.
3847
- * Runs entirely client-side (canvas, no Host Shell round-trip) and returns
3848
- * a base64 PNG data URI, ready for an `<img src>` or `media.saveImage()`.
3849
- * Mini-Apps must not reimplement this on their own.
3850
- */
3851
- qrCode = Object.freeze({
3852
- /**
3853
- * @param data The value to encode (e.g. a deep link).
3854
- * @param options Styling/labeling options — see {@link IGenerateQrCodeOptions}.
3855
- * @returns A base64 PNG data URI.
3856
- */
3857
- generate: (data, options) => generateQrCode(data, options),
3858
- /**
3859
- * Decodes a QR code from a static image (base64 data URI or URL) —
3860
- * e.g. a photo picked via `sdk.media.pickImage()` that already
3861
- * contains a QR code, as an alternative to the live `<QRScanner>`.
3862
- * @param imageDataUrl A base64 data URI (e.g. from `sdk.media.pickImage()`).
3863
- * @returns The decoded string, or `null` if no QR code was found.
3864
- */
3865
- decode: (imageDataUrl) => decodeQrCode(imageDataUrl)
3866
- });
3867
3870
  /**
3868
3871
  * Payments integration.
3869
3872
  */
@@ -4155,6 +4158,40 @@ function getMockAvatarUrl(seed) {
4155
4158
  const shortSeed = hashToShortSeed(seed);
4156
4159
  return `https://api.dicebear.com/10.x/notionists-neutral/png?seed=${shortSeed}&mouth=${AVATAR_MOUTH_VARIANTS}&eyes=${AVATAR_EYES_VARIANTS}&glassesProbability=50`;
4157
4160
  }
4161
+ function decodeMockJWTPayload(token) {
4162
+ try {
4163
+ const raw = token.startsWith("Bearer ") ? token.slice(7) : token;
4164
+ const base64Url = raw.split(".")[1];
4165
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
4166
+ const jsonPayload = decodeURIComponent(
4167
+ atob(base64).split("").map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`).join("")
4168
+ );
4169
+ return JSON.parse(jsonPayload);
4170
+ } catch {
4171
+ return null;
4172
+ }
4173
+ }
4174
+ function buildMockProfile(mockJWTToken) {
4175
+ const claims = mockJWTToken ? decodeMockJWTPayload(mockJWTToken) : null;
4176
+ const uuid = claims?.sub || "mock-user-42";
4177
+ const fullName = claims?.name || claims?.username || "Smart Caveman";
4178
+ const username = claims?.username || "smart_caveman";
4179
+ const [firstName, ...lastNameParts] = fullName.split(" ");
4180
+ return {
4181
+ admin: true,
4182
+ countryCode: "FR",
4183
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4184
+ email: "caveman@up.app",
4185
+ firstName: firstName || "Smart",
4186
+ fullName,
4187
+ lastName: lastNameParts.join(" ") || "Caveman",
4188
+ localNumber: "0600000000",
4189
+ ups: 100,
4190
+ username,
4191
+ uuid,
4192
+ xp: 500
4193
+ };
4194
+ }
4158
4195
  var getBearing = (p1, p2) => {
4159
4196
  const lat1 = p1[1] * Math.PI / 180;
4160
4197
  const lat2 = p2[1] * Math.PI / 180;
@@ -4386,20 +4423,7 @@ var BridgeMocker = ({
4386
4423
  insets: { bottom: 0, left: 0, right: 0, top: 0 },
4387
4424
  name: "Vite Dev (Mock)",
4388
4425
  params: {},
4389
- profile: {
4390
- admin: true,
4391
- countryCode: "FR",
4392
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4393
- email: "caveman@up.app",
4394
- firstName: "Smart",
4395
- fullName: "Smart Caveman",
4396
- lastName: "Caveman",
4397
- localNumber: "0600000000",
4398
- ups: 100,
4399
- username: "smart_caveman",
4400
- uuid: "mock-user-42",
4401
- xp: 500
4402
- },
4426
+ profile: buildMockProfile(stateRef.current.mockJWTToken),
4403
4427
  success: true,
4404
4428
  userData: {},
4405
4429
  uuid: appId || "mock-app-id",
@@ -4440,20 +4464,7 @@ var BridgeMocker = ({
4440
4464
  return Promise.resolve();
4441
4465
  }
4442
4466
  if (type === "GET_USER_PROFILE") {
4443
- return Promise.resolve({
4444
- admin: true,
4445
- countryCode: "FR",
4446
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4447
- email: "caveman@up.app",
4448
- firstName: "Smart",
4449
- fullName: "Smart Caveman",
4450
- lastName: "Caveman",
4451
- localNumber: "0600000000",
4452
- ups: 100,
4453
- username: "smart_caveman",
4454
- uuid: "mock-user-42",
4455
- xp: 500
4456
- });
4467
+ return Promise.resolve(buildMockProfile(stateRef.current.mockJWTToken));
4457
4468
  }
4458
4469
  if (type === "GET_PROFILE_PICTURE") {
4459
4470
  const uuids = payload?.uuids || [];
@@ -5377,6 +5388,13 @@ function createWsService(options) {
5377
5388
  let reconnectTimeout = null;
5378
5389
  let reconnectDelay = 1e3;
5379
5390
  let isConnecting = false;
5391
+ let status = "closed";
5392
+ const statusListeners = /* @__PURE__ */ new Set();
5393
+ const setStatus = (next) => {
5394
+ if (status === next) return;
5395
+ status = next;
5396
+ for (const cb of statusListeners) cb(status);
5397
+ };
5380
5398
  const getWsUrl = () => options.baseUrl || options.api.wsUrl || "";
5381
5399
  const sendAction = (action, channelId) => {
5382
5400
  if (ws?.readyState === WebSocket.OPEN) {
@@ -5395,6 +5413,7 @@ function createWsService(options) {
5395
5413
  return;
5396
5414
  }
5397
5415
  isConnecting = true;
5416
+ setStatus("connecting");
5398
5417
  try {
5399
5418
  const wsUrl = getWsUrl();
5400
5419
  if (!wsUrl) {
@@ -5402,6 +5421,7 @@ function createWsService(options) {
5402
5421
  "[WS] No WebSocket backend URL available, connection delayed."
5403
5422
  );
5404
5423
  isConnecting = false;
5424
+ setStatus("closed");
5405
5425
  return;
5406
5426
  }
5407
5427
  let authParams = {};
@@ -5417,6 +5437,7 @@ function createWsService(options) {
5417
5437
  } else {
5418
5438
  console.warn("[WS] No auth available, connection delayed.");
5419
5439
  isConnecting = false;
5440
+ setStatus("closed");
5420
5441
  return;
5421
5442
  }
5422
5443
  const params = new URLSearchParams({ ...options.query, ...authParams });
@@ -5424,6 +5445,7 @@ function createWsService(options) {
5424
5445
  ws = new WebSocket(query ? `${wsUrl}?${query}` : wsUrl);
5425
5446
  ws.onopen = () => {
5426
5447
  isConnecting = false;
5448
+ setStatus("open");
5427
5449
  reconnectDelay = 1e3;
5428
5450
  if (channelField) {
5429
5451
  for (const channelId of subscriptions.keys()) {
@@ -5450,6 +5472,7 @@ function createWsService(options) {
5450
5472
  };
5451
5473
  ws.onclose = () => {
5452
5474
  isConnecting = false;
5475
+ setStatus("closed");
5453
5476
  scheduleReconnect();
5454
5477
  };
5455
5478
  ws.onerror = (err) => {
@@ -5460,6 +5483,7 @@ function createWsService(options) {
5460
5483
  } catch (err) {
5461
5484
  console.error("[WS] Failed to establish connection:", err);
5462
5485
  isConnecting = false;
5486
+ setStatus("closed");
5463
5487
  scheduleReconnect();
5464
5488
  }
5465
5489
  };
@@ -5473,6 +5497,15 @@ function createWsService(options) {
5473
5497
  ws.close();
5474
5498
  ws = null;
5475
5499
  }
5500
+ setStatus("closed");
5501
+ };
5502
+ const getStatus = () => status;
5503
+ const onStatusChange = (callback) => {
5504
+ statusListeners.add(callback);
5505
+ callback(status);
5506
+ return () => {
5507
+ statusListeners.delete(callback);
5508
+ };
5476
5509
  };
5477
5510
  const subscribe = (channelId, callback) => {
5478
5511
  const isNewChannel = !subscriptions.has(channelId);
@@ -5498,9 +5531,16 @@ function createWsService(options) {
5498
5531
  };
5499
5532
  };
5500
5533
  if (channelField) {
5501
- return { connect, disconnect, onMessage, subscribe };
5534
+ return {
5535
+ connect,
5536
+ disconnect,
5537
+ getStatus,
5538
+ onMessage,
5539
+ onStatusChange,
5540
+ subscribe
5541
+ };
5502
5542
  }
5503
- return { connect, disconnect, onMessage };
5543
+ return { connect, disconnect, getStatus, onMessage, onStatusChange };
5504
5544
  }
5505
5545
 
5506
5546
  // src/index.ts