@getpara/core-sdk 3.0.0 → 3.2.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.
@@ -265,10 +265,11 @@ const _ParaCore = class _ParaCore {
265
265
  if (typeof original === "function") {
266
266
  this[methodName] = (...args) => {
267
267
  var _a;
268
- const attrs = __spreadValues({
269
- "para.platform": this.platformUtils.sdkType,
268
+ const attrs = __spreadValues(__spreadProps(__spreadValues({
269
+ "para.platform": this.platformUtils.sdkType
270
+ }, this.clientType ? { "para.client_type": this.clientType } : {}), {
270
271
  "para.sdk_version": _ParaCore.version
271
- }, ((_a = this.partner) == null ? void 0 : _a.id) ? { "para.partner_id": this.partner.id } : {});
272
+ }), ((_a = this.partner) == null ? void 0 : _a.id) ? { "para.partner_id": this.partner.id } : {});
272
273
  return (0, import_tracer.wrapWithSpan)(
273
274
  methodName,
274
275
  (span) => __async(this, null, function* () {
@@ -430,6 +431,7 @@ const _ParaCore = class _ParaCore {
430
431
  this.addCredential = (params) => __async(this, null, function* () {
431
432
  return yield __privateGet(this, _authService).addCredential(params);
432
433
  });
434
+ var _a;
433
435
  let env, apiKey;
434
436
  const actualArgs = Array.from(arguments).filter((arg) => arg !== void 0);
435
437
  const actualArgumentCount = actualArgs.length;
@@ -514,11 +516,16 @@ const _ParaCore = class _ParaCore {
514
516
  refreshJwt
515
517
  );
516
518
  };
519
+ const rawPlatform = (_a = opts.hostPlatform) != null ? _a : this.platformUtils.sdkType;
520
+ this.clientType = (0, import_userManagementClient.normalizePlatform)(rawPlatform);
517
521
  const client = (0, import_userManagementClient.initClient)({
518
522
  env,
519
523
  version: _ParaCore.version,
520
524
  apiKey,
521
525
  partnerId: this.isPortal(env) ? opts.portalPartnerId : void 0,
526
+ // Native shells (Swift/Flutter) set opts.hostPlatform via bridge-v2; web and
527
+ // React Native fall back to the in-process sdkType. Normalized in initClient.
528
+ platform: rawPlatform,
522
529
  useFetchAdapter: !!opts.disableWorkers,
523
530
  retrieveSessionCookie: this.retrieveSessionCookie,
524
531
  persistSessionCookie: this.persistSessionCookie
@@ -1570,6 +1577,7 @@ const _ParaCore = class _ParaCore {
1570
1577
  tunnelUrl: `${(0, import_userManagementClient.getBaseUrl)(this.ctx.env)}telemetry`,
1571
1578
  sampleRate: telemetry.sample_rate,
1572
1579
  sdkType: this.platformUtils.sdkType,
1580
+ clientType: this.clientType,
1573
1581
  sdkVersion: _ParaCore.version,
1574
1582
  partnerId: this.partner.id,
1575
1583
  // Only true in DEV — adds localhost to the trace-propagation allow-list
@@ -46,7 +46,7 @@ __export(constants_exports, {
46
46
  TRANSACTION_REVIEW_TIMEOUT_MS: () => TRANSACTION_REVIEW_TIMEOUT_MS
47
47
  });
48
48
  module.exports = __toCommonJS(constants_exports);
49
- const PARA_CORE_VERSION = "3.0.0";
49
+ const PARA_CORE_VERSION = "3.2.0";
50
50
  const PREFIX = "@CAPSULE/";
51
51
  const PARA_PREFIX = "@PARA/";
52
52
  const LOCAL_STORAGE_AUTH_INFO = `${PREFIX}authInfo`;
@@ -30,7 +30,8 @@ __export(userManagementClient_exports, {
30
30
  getBaseMPCNetworkUrl: () => getBaseMPCNetworkUrl,
31
31
  getBaseOAuthUrl: () => getBaseOAuthUrl,
32
32
  getBaseUrl: () => getBaseUrl,
33
- initClient: () => initClient
33
+ initClient: () => initClient,
34
+ normalizePlatform: () => normalizePlatform
34
35
  });
35
36
  module.exports = __toCommonJS(userManagementClient_exports);
36
37
  var import_user_management_client = __toESM(require("@getpara/user-management-client"));
@@ -93,11 +94,33 @@ function getBaseMPCNetworkUrl(env, useWebsocket) {
93
94
  throw new Error(`unsupported env: ${env}`);
94
95
  }
95
96
  }
97
+ function normalizePlatform(platform) {
98
+ switch (platform) {
99
+ case "iOS":
100
+ case "ios":
101
+ case "swift":
102
+ return "swift";
103
+ case "flutter":
104
+ return "flutter";
105
+ case "REACT_NATIVE":
106
+ case "react-native":
107
+ return "react-native";
108
+ case "WEB":
109
+ case "web":
110
+ return "web";
111
+ case "SERVER":
112
+ case "server":
113
+ return "server";
114
+ default:
115
+ return void 0;
116
+ }
117
+ }
96
118
  function initClient({
97
119
  env,
98
120
  version,
99
121
  apiKey,
100
122
  partnerId,
123
+ platform,
101
124
  useFetchAdapter = false,
102
125
  retrieveSessionCookie,
103
126
  persistSessionCookie,
@@ -108,6 +131,7 @@ function initClient({
108
131
  version: [import_types.Environment.DEV, import_types.Environment.SANDBOX].includes(env) ? "dev" : version,
109
132
  apiKey,
110
133
  partnerId,
134
+ clientType: normalizePlatform(platform),
111
135
  opts: { useFetchAdapter },
112
136
  retrieveSessionCookie,
113
137
  persistSessionCookie,
@@ -119,5 +143,6 @@ function initClient({
119
143
  getBaseMPCNetworkUrl,
120
144
  getBaseOAuthUrl,
121
145
  getBaseUrl,
122
- initClient
146
+ initClient,
147
+ normalizePlatform
123
148
  });
@@ -412,6 +412,8 @@ class PollingService {
412
412
  if (__privateGet(this, _walletService).currentWalletIdsArray.length === 0) {
413
413
  return { finished: false };
414
414
  }
415
+ } else if (__privateGet(this, _walletService).currentWalletIdsArray.length === 0) {
416
+ return { finished: true };
415
417
  }
416
418
  const sessionLookupId = session.sessionLookupId;
417
419
  const temporaryShares = (yield __privateGet(this, _paraCoreInterface).ctx.client.getTransmissionKeyshares(__privateGet(this, _authService).userId, sessionLookupId)).data.temporaryShares;
@@ -1,10 +1,24 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
4
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
7
  var __typeError = (msg) => {
6
8
  throw TypeError(msg);
7
9
  };
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
8
22
  var __export = (target, all) => {
9
23
  for (var name in all)
10
24
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -49,8 +63,8 @@ __export(SessionManagementService_exports, {
49
63
  module.exports = __toCommonJS(SessionManagementService_exports);
50
64
  var import_wallet = require("../utils/wallet.js");
51
65
  var import_tracer = require("../telemetry/tracer.js");
52
- var _authService, _portalUrlService, _walletService, _pregenWalletService, _externalWalletService, _paraCoreInterface, _isImportedSession, _regeneratePromise, _handleTouchSessionError, _doTouchSession;
53
- class SessionManagementService {
66
+ var _authService, _portalUrlService, _walletService, _pregenWalletService, _externalWalletService, _paraCoreInterface, _isImportedSession, _regeneratePromise, _touchPromise, _touchCachedAt, _touchCached, _TOUCH_TTL_MS, _handleTouchSessionError, _shouldDedupeTouch, _doTouchSession;
67
+ const _SessionManagementService = class _SessionManagementService {
54
68
  constructor(paraCore) {
55
69
  __privateAdd(this, _authService);
56
70
  __privateAdd(this, _portalUrlService);
@@ -60,6 +74,21 @@ class SessionManagementService {
60
74
  __privateAdd(this, _paraCoreInterface);
61
75
  __privateAdd(this, _isImportedSession);
62
76
  __privateAdd(this, _regeneratePromise);
77
+ // In-flight + short TTL dedupe for touchSession(regenerate=false). Prevents
78
+ // the post-login storm where useIsFullyLoggedIn → isFullyLoggedIn →
79
+ // isSessionActive → touchSession() fires once per render across many
80
+ // consumers (we observed 68 /touch round-trips in a 663ms window during
81
+ // AccountReady). The result is still authoritative — just shared across
82
+ // concurrent and immediately-following callers instead of refetched per call.
83
+ //
84
+ // IMPORTANT: this is disabled in the portal (see #shouldDedupeTouch). The
85
+ // portal reuses this same service and depends on touchSession() running its
86
+ // side-effects (currentWalletIds / partner sync) and returning fresh state on
87
+ // every sequential poll; a previous unconditional version of this cache
88
+ // (eb8e5f843, reverted in 58c8dadd6) broke that portal flow.
89
+ __privateAdd(this, _touchPromise);
90
+ __privateAdd(this, _touchCachedAt);
91
+ __privateAdd(this, _touchCached);
63
92
  this.init = ({
64
93
  authService,
65
94
  portalUrlService,
@@ -97,6 +126,15 @@ class SessionManagementService {
97
126
  if (regenerate && __privateGet(this, _regeneratePromise) !== void 0) {
98
127
  return __privateGet(this, _regeneratePromise);
99
128
  }
129
+ const dedupe = __privateGet(this, _shouldDedupeTouch).call(this, regenerate);
130
+ if (dedupe) {
131
+ if (__privateGet(this, _touchPromise) !== void 0) {
132
+ return __privateGet(this, _touchPromise);
133
+ }
134
+ if (__privateGet(this, _touchCached) !== void 0 && __privateGet(this, _touchCachedAt) !== void 0 && Date.now() - __privateGet(this, _touchCachedAt) < __privateGet(_SessionManagementService, _TOUCH_TTL_MS)) {
135
+ return __spreadValues({}, __privateGet(this, _touchCached));
136
+ }
137
+ }
100
138
  return (0, import_tracer.wrapWithSpan)(
101
139
  "session.touch",
102
140
  () => __async(this, null, function* () {
@@ -105,13 +143,32 @@ class SessionManagementService {
105
143
  __privateSet(this, _regeneratePromise, promise);
106
144
  yield promise.finally(() => {
107
145
  __privateSet(this, _regeneratePromise, void 0);
146
+ __privateSet(this, _touchCached, void 0);
147
+ __privateSet(this, _touchCachedAt, void 0);
108
148
  });
149
+ } else if (dedupe) {
150
+ __privateSet(this, _touchPromise, promise);
151
+ try {
152
+ const result = yield promise;
153
+ if (result.userId) {
154
+ __privateSet(this, _touchCached, result);
155
+ __privateSet(this, _touchCachedAt, Date.now());
156
+ }
157
+ } finally {
158
+ __privateSet(this, _touchPromise, void 0);
159
+ }
109
160
  }
110
161
  return promise;
111
162
  }),
112
163
  { "session.regenerate": regenerate }
113
164
  );
114
165
  });
166
+ // Whether the non-regenerate dedupe/cache should apply. Disabled in the portal,
167
+ // which depends on every touchSession() call running its side-effects and
168
+ // returning fresh session state (see #touchCached docs).
169
+ __privateAdd(this, _shouldDedupeTouch, (regenerate) => {
170
+ return !regenerate && !__privateGet(this, _paraCoreInterface).isPortal();
171
+ });
115
172
  __privateAdd(this, _doTouchSession, (regenerate) => __async(this, null, function* () {
116
173
  var _a, _b;
117
174
  if (!__privateGet(this, _paraCoreInterface).isWorkerInitialized) {
@@ -270,7 +327,7 @@ class SessionManagementService {
270
327
  set isImportedSession(value) {
271
328
  __privateSet(this, _isImportedSession, value);
272
329
  }
273
- }
330
+ };
274
331
  _authService = new WeakMap();
275
332
  _portalUrlService = new WeakMap();
276
333
  _walletService = new WeakMap();
@@ -279,8 +336,15 @@ _externalWalletService = new WeakMap();
279
336
  _paraCoreInterface = new WeakMap();
280
337
  _isImportedSession = new WeakMap();
281
338
  _regeneratePromise = new WeakMap();
339
+ _touchPromise = new WeakMap();
340
+ _touchCachedAt = new WeakMap();
341
+ _touchCached = new WeakMap();
342
+ _TOUCH_TTL_MS = new WeakMap();
282
343
  _handleTouchSessionError = new WeakMap();
344
+ _shouldDedupeTouch = new WeakMap();
283
345
  _doTouchSession = new WeakMap();
346
+ __privateAdd(_SessionManagementService, _TOUCH_TTL_MS, 1e3);
347
+ let SessionManagementService = _SessionManagementService;
284
348
  // Annotate the CommonJS export names for ESM import in node:
285
349
  0 && (module.exports = {
286
350
  SessionManagementService
@@ -0,0 +1,99 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __typeError = (msg) => {
6
+ throw TypeError(msg);
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
22
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
23
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
24
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
25
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
26
+ var BeaconSpanProcessor_exports = {};
27
+ __export(BeaconSpanProcessor_exports, {
28
+ BeaconSpanProcessor: () => BeaconSpanProcessor
29
+ });
30
+ module.exports = __toCommonJS(BeaconSpanProcessor_exports);
31
+ var import_otlp_transformer = require("@opentelemetry/otlp-transformer");
32
+ var _spans, _pagehideListener, _visibilityListener, _url, _BeaconSpanProcessor_instances, flushViaBeacon_fn;
33
+ const MAX_BUFFER_SIZE = 200;
34
+ class BeaconSpanProcessor {
35
+ constructor(url) {
36
+ __privateAdd(this, _BeaconSpanProcessor_instances);
37
+ __privateAdd(this, _spans, []);
38
+ __privateAdd(this, _pagehideListener);
39
+ __privateAdd(this, _visibilityListener);
40
+ __privateAdd(this, _url);
41
+ __privateSet(this, _url, url);
42
+ if (typeof window !== "undefined") {
43
+ __privateSet(this, _pagehideListener, () => __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this));
44
+ __privateSet(this, _visibilityListener, () => {
45
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
46
+ __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this);
47
+ }
48
+ });
49
+ window.addEventListener("pagehide", __privateGet(this, _pagehideListener));
50
+ if (typeof document !== "undefined") {
51
+ document.addEventListener("visibilitychange", __privateGet(this, _visibilityListener));
52
+ }
53
+ }
54
+ }
55
+ onStart(_span, _parentContext) {
56
+ }
57
+ onEnd(span) {
58
+ __privateGet(this, _spans).push(span);
59
+ if (__privateGet(this, _spans).length > MAX_BUFFER_SIZE) {
60
+ __privateGet(this, _spans).shift();
61
+ }
62
+ }
63
+ forceFlush() {
64
+ return Promise.resolve();
65
+ }
66
+ shutdown() {
67
+ __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this);
68
+ if (__privateGet(this, _pagehideListener) && typeof window !== "undefined") {
69
+ window.removeEventListener("pagehide", __privateGet(this, _pagehideListener));
70
+ __privateSet(this, _pagehideListener, void 0);
71
+ }
72
+ if (__privateGet(this, _visibilityListener) && typeof document !== "undefined") {
73
+ document.removeEventListener("visibilitychange", __privateGet(this, _visibilityListener));
74
+ __privateSet(this, _visibilityListener, void 0);
75
+ }
76
+ __privateSet(this, _spans, []);
77
+ return Promise.resolve();
78
+ }
79
+ }
80
+ _spans = new WeakMap();
81
+ _pagehideListener = new WeakMap();
82
+ _visibilityListener = new WeakMap();
83
+ _url = new WeakMap();
84
+ _BeaconSpanProcessor_instances = new WeakSet();
85
+ flushViaBeacon_fn = function() {
86
+ if (__privateGet(this, _spans).length === 0) return;
87
+ if (typeof navigator === "undefined" || typeof navigator.sendBeacon !== "function") return;
88
+ try {
89
+ const body = import_otlp_transformer.JsonTraceSerializer.serializeRequest(__privateGet(this, _spans));
90
+ const blob = new Blob([body], { type: "application/json" });
91
+ navigator.sendBeacon(__privateGet(this, _url), blob);
92
+ __privateSet(this, _spans, []);
93
+ } catch (e) {
94
+ }
95
+ };
96
+ // Annotate the CommonJS export names for ESM import in node:
97
+ 0 && (module.exports = {
98
+ BeaconSpanProcessor
99
+ });
@@ -99,21 +99,29 @@ function initTelemetry(opts) {
99
99
  } catch (e) {
100
100
  }
101
101
  }
102
- const resource = (0, import_resources.resourceFromAttributes)(__spreadValues(__spreadValues({
102
+ const resource = (0, import_resources.resourceFromAttributes)(__spreadValues(__spreadValues(__spreadValues({
103
103
  [import_semantic_conventions.ATTR_SERVICE_NAME]: opts.isPortal ? "para-portal" : "para-sdk",
104
104
  [import_semantic_conventions.ATTR_SERVICE_VERSION]: opts.sdkVersion,
105
105
  "para.platform": opts.sdkType
106
- }, opts.partnerId ? { "para.partner_id": opts.partnerId } : {}), opts.resourceAttributes));
106
+ }, opts.clientType ? { "para.client_type": opts.clientType } : {}), opts.partnerId ? { "para.partner_id": opts.partnerId } : {}), opts.resourceAttributes));
107
107
  const exporter = new import_exporter_trace_otlp_http.OTLPTraceExporter({
108
108
  url: opts.tunnelUrl
109
109
  // No auth header — the tunnel is a dumb proxy. Per-IP rate limit + body cap on the
110
110
  // BE side keep abuse contained.
111
111
  });
112
112
  const sampler = new import_sdk_trace_base.TraceIdRatioBasedSampler(Math.max(0, Math.min(1, opts.sampleRate)));
113
+ const spanProcessors = [new import_uxStateSpanProcessor.UxStateSpanProcessor(), new import_sdk_trace_base.BatchSpanProcessor(exporter)];
114
+ if (isWeb && opts.isPortal) {
115
+ try {
116
+ const { BeaconSpanProcessor } = yield import("./BeaconSpanProcessor.js");
117
+ spanProcessors.push(new BeaconSpanProcessor(opts.tunnelUrl));
118
+ } catch (e) {
119
+ }
120
+ }
113
121
  tracerProvider = new import_sdk_trace_base.BasicTracerProvider({
114
122
  resource,
115
123
  sampler,
116
- spanProcessors: [new import_uxStateSpanProcessor.UxStateSpanProcessor(), new import_sdk_trace_base.BatchSpanProcessor(exporter)]
124
+ spanProcessors
117
125
  });
118
126
  import_api.trace.setGlobalTracerProvider(tracerProvider);
119
127
  import_api.propagation.setGlobalPropagator(
@@ -29,7 +29,7 @@ import {
29
29
  import forge from "node-forge";
30
30
  const { pki, jsbn } = forge;
31
31
  import { decryptWithPrivateKey, getAsymmetricKeyPair } from "./cryptography/utils.js";
32
- import { getBaseUrl, initClient } from "./external/userManagementClient.js";
32
+ import { getBaseUrl, initClient, normalizePlatform } from "./external/userManagementClient.js";
33
33
  import * as mpcComputationClient from "./external/mpcComputationClient.js";
34
34
  import {
35
35
  Environment,
@@ -210,10 +210,11 @@ const _ParaCore = class _ParaCore {
210
210
  if (typeof original === "function") {
211
211
  this[methodName] = (...args) => {
212
212
  var _a;
213
- const attrs = __spreadValues({
214
- "para.platform": this.platformUtils.sdkType,
213
+ const attrs = __spreadValues(__spreadProps(__spreadValues({
214
+ "para.platform": this.platformUtils.sdkType
215
+ }, this.clientType ? { "para.client_type": this.clientType } : {}), {
215
216
  "para.sdk_version": _ParaCore.version
216
- }, ((_a = this.partner) == null ? void 0 : _a.id) ? { "para.partner_id": this.partner.id } : {});
217
+ }), ((_a = this.partner) == null ? void 0 : _a.id) ? { "para.partner_id": this.partner.id } : {});
217
218
  return wrapWithSpan(
218
219
  methodName,
219
220
  (span) => __async(this, null, function* () {
@@ -375,6 +376,7 @@ const _ParaCore = class _ParaCore {
375
376
  this.addCredential = (params) => __async(this, null, function* () {
376
377
  return yield __privateGet(this, _authService).addCredential(params);
377
378
  });
379
+ var _a;
378
380
  let env, apiKey;
379
381
  const actualArgs = Array.from(arguments).filter((arg) => arg !== void 0);
380
382
  const actualArgumentCount = actualArgs.length;
@@ -459,11 +461,16 @@ const _ParaCore = class _ParaCore {
459
461
  refreshJwt
460
462
  );
461
463
  };
464
+ const rawPlatform = (_a = opts.hostPlatform) != null ? _a : this.platformUtils.sdkType;
465
+ this.clientType = normalizePlatform(rawPlatform);
462
466
  const client = initClient({
463
467
  env,
464
468
  version: _ParaCore.version,
465
469
  apiKey,
466
470
  partnerId: this.isPortal(env) ? opts.portalPartnerId : void 0,
471
+ // Native shells (Swift/Flutter) set opts.hostPlatform via bridge-v2; web and
472
+ // React Native fall back to the in-process sdkType. Normalized in initClient.
473
+ platform: rawPlatform,
467
474
  useFetchAdapter: !!opts.disableWorkers,
468
475
  retrieveSessionCookie: this.retrieveSessionCookie,
469
476
  persistSessionCookie: this.persistSessionCookie
@@ -1515,6 +1522,7 @@ const _ParaCore = class _ParaCore {
1515
1522
  tunnelUrl: `${getBaseUrl(this.ctx.env)}telemetry`,
1516
1523
  sampleRate: telemetry.sample_rate,
1517
1524
  sdkType: this.platformUtils.sdkType,
1525
+ clientType: this.clientType,
1518
1526
  sdkVersion: _ParaCore.version,
1519
1527
  partnerId: this.partner.id,
1520
1528
  // Only true in DEV — adds localhost to the trace-propagation allow-list
@@ -1,5 +1,5 @@
1
1
  import "./chunk-7B52C2XE.js";
2
- const PARA_CORE_VERSION = "3.0.0";
2
+ const PARA_CORE_VERSION = "3.2.0";
3
3
  const PREFIX = "@CAPSULE/";
4
4
  const PARA_PREFIX = "@PARA/";
5
5
  const LOCAL_STORAGE_AUTH_INFO = `${PREFIX}authInfo`;
@@ -59,11 +59,33 @@ function getBaseMPCNetworkUrl(env, useWebsocket) {
59
59
  throw new Error(`unsupported env: ${env}`);
60
60
  }
61
61
  }
62
+ function normalizePlatform(platform) {
63
+ switch (platform) {
64
+ case "iOS":
65
+ case "ios":
66
+ case "swift":
67
+ return "swift";
68
+ case "flutter":
69
+ return "flutter";
70
+ case "REACT_NATIVE":
71
+ case "react-native":
72
+ return "react-native";
73
+ case "WEB":
74
+ case "web":
75
+ return "web";
76
+ case "SERVER":
77
+ case "server":
78
+ return "server";
79
+ default:
80
+ return void 0;
81
+ }
82
+ }
62
83
  function initClient({
63
84
  env,
64
85
  version,
65
86
  apiKey,
66
87
  partnerId,
88
+ platform,
67
89
  useFetchAdapter = false,
68
90
  retrieveSessionCookie,
69
91
  persistSessionCookie,
@@ -74,6 +96,7 @@ function initClient({
74
96
  version: [Environment.DEV, Environment.SANDBOX].includes(env) ? "dev" : version,
75
97
  apiKey,
76
98
  partnerId,
99
+ clientType: normalizePlatform(platform),
77
100
  opts: { useFetchAdapter },
78
101
  retrieveSessionCookie,
79
102
  persistSessionCookie,
@@ -84,5 +107,6 @@ export {
84
107
  getBaseMPCNetworkUrl,
85
108
  getBaseOAuthUrl,
86
109
  getBaseUrl,
87
- initClient
110
+ initClient,
111
+ normalizePlatform
88
112
  };
@@ -354,6 +354,8 @@ class PollingService {
354
354
  if (__privateGet(this, _walletService).currentWalletIdsArray.length === 0) {
355
355
  return { finished: false };
356
356
  }
357
+ } else if (__privateGet(this, _walletService).currentWalletIdsArray.length === 0) {
358
+ return { finished: true };
357
359
  }
358
360
  const sessionLookupId = session.sessionLookupId;
359
361
  const temporaryShares = (yield __privateGet(this, _paraCoreInterface).ctx.client.getTransmissionKeyshares(__privateGet(this, _authService).userId, sessionLookupId)).data.temporaryShares;
@@ -2,12 +2,13 @@ import {
2
2
  __async,
3
3
  __privateAdd,
4
4
  __privateGet,
5
- __privateSet
5
+ __privateSet,
6
+ __spreadValues
6
7
  } from "../chunk-7B52C2XE.js";
7
- var _authService, _portalUrlService, _walletService, _pregenWalletService, _externalWalletService, _paraCoreInterface, _isImportedSession, _regeneratePromise, _handleTouchSessionError, _doTouchSession;
8
+ var _authService, _portalUrlService, _walletService, _pregenWalletService, _externalWalletService, _paraCoreInterface, _isImportedSession, _regeneratePromise, _touchPromise, _touchCachedAt, _touchCached, _TOUCH_TTL_MS, _handleTouchSessionError, _shouldDedupeTouch, _doTouchSession;
8
9
  import { currentWalletIdsEq, supportedWalletTypesEq } from "../utils/wallet.js";
9
10
  import { wrapWithSpan } from "../telemetry/tracer.js";
10
- class SessionManagementService {
11
+ const _SessionManagementService = class _SessionManagementService {
11
12
  constructor(paraCore) {
12
13
  __privateAdd(this, _authService);
13
14
  __privateAdd(this, _portalUrlService);
@@ -17,6 +18,21 @@ class SessionManagementService {
17
18
  __privateAdd(this, _paraCoreInterface);
18
19
  __privateAdd(this, _isImportedSession);
19
20
  __privateAdd(this, _regeneratePromise);
21
+ // In-flight + short TTL dedupe for touchSession(regenerate=false). Prevents
22
+ // the post-login storm where useIsFullyLoggedIn → isFullyLoggedIn →
23
+ // isSessionActive → touchSession() fires once per render across many
24
+ // consumers (we observed 68 /touch round-trips in a 663ms window during
25
+ // AccountReady). The result is still authoritative — just shared across
26
+ // concurrent and immediately-following callers instead of refetched per call.
27
+ //
28
+ // IMPORTANT: this is disabled in the portal (see #shouldDedupeTouch). The
29
+ // portal reuses this same service and depends on touchSession() running its
30
+ // side-effects (currentWalletIds / partner sync) and returning fresh state on
31
+ // every sequential poll; a previous unconditional version of this cache
32
+ // (eb8e5f843, reverted in 58c8dadd6) broke that portal flow.
33
+ __privateAdd(this, _touchPromise);
34
+ __privateAdd(this, _touchCachedAt);
35
+ __privateAdd(this, _touchCached);
20
36
  this.init = ({
21
37
  authService,
22
38
  portalUrlService,
@@ -54,6 +70,15 @@ class SessionManagementService {
54
70
  if (regenerate && __privateGet(this, _regeneratePromise) !== void 0) {
55
71
  return __privateGet(this, _regeneratePromise);
56
72
  }
73
+ const dedupe = __privateGet(this, _shouldDedupeTouch).call(this, regenerate);
74
+ if (dedupe) {
75
+ if (__privateGet(this, _touchPromise) !== void 0) {
76
+ return __privateGet(this, _touchPromise);
77
+ }
78
+ if (__privateGet(this, _touchCached) !== void 0 && __privateGet(this, _touchCachedAt) !== void 0 && Date.now() - __privateGet(this, _touchCachedAt) < __privateGet(_SessionManagementService, _TOUCH_TTL_MS)) {
79
+ return __spreadValues({}, __privateGet(this, _touchCached));
80
+ }
81
+ }
57
82
  return wrapWithSpan(
58
83
  "session.touch",
59
84
  () => __async(this, null, function* () {
@@ -62,13 +87,32 @@ class SessionManagementService {
62
87
  __privateSet(this, _regeneratePromise, promise);
63
88
  yield promise.finally(() => {
64
89
  __privateSet(this, _regeneratePromise, void 0);
90
+ __privateSet(this, _touchCached, void 0);
91
+ __privateSet(this, _touchCachedAt, void 0);
65
92
  });
93
+ } else if (dedupe) {
94
+ __privateSet(this, _touchPromise, promise);
95
+ try {
96
+ const result = yield promise;
97
+ if (result.userId) {
98
+ __privateSet(this, _touchCached, result);
99
+ __privateSet(this, _touchCachedAt, Date.now());
100
+ }
101
+ } finally {
102
+ __privateSet(this, _touchPromise, void 0);
103
+ }
66
104
  }
67
105
  return promise;
68
106
  }),
69
107
  { "session.regenerate": regenerate }
70
108
  );
71
109
  });
110
+ // Whether the non-regenerate dedupe/cache should apply. Disabled in the portal,
111
+ // which depends on every touchSession() call running its side-effects and
112
+ // returning fresh session state (see #touchCached docs).
113
+ __privateAdd(this, _shouldDedupeTouch, (regenerate) => {
114
+ return !regenerate && !__privateGet(this, _paraCoreInterface).isPortal();
115
+ });
72
116
  __privateAdd(this, _doTouchSession, (regenerate) => __async(this, null, function* () {
73
117
  var _a, _b;
74
118
  if (!__privateGet(this, _paraCoreInterface).isWorkerInitialized) {
@@ -227,7 +271,7 @@ class SessionManagementService {
227
271
  set isImportedSession(value) {
228
272
  __privateSet(this, _isImportedSession, value);
229
273
  }
230
- }
274
+ };
231
275
  _authService = new WeakMap();
232
276
  _portalUrlService = new WeakMap();
233
277
  _walletService = new WeakMap();
@@ -236,8 +280,15 @@ _externalWalletService = new WeakMap();
236
280
  _paraCoreInterface = new WeakMap();
237
281
  _isImportedSession = new WeakMap();
238
282
  _regeneratePromise = new WeakMap();
283
+ _touchPromise = new WeakMap();
284
+ _touchCachedAt = new WeakMap();
285
+ _touchCached = new WeakMap();
286
+ _TOUCH_TTL_MS = new WeakMap();
239
287
  _handleTouchSessionError = new WeakMap();
288
+ _shouldDedupeTouch = new WeakMap();
240
289
  _doTouchSession = new WeakMap();
290
+ __privateAdd(_SessionManagementService, _TOUCH_TTL_MS, 1e3);
291
+ let SessionManagementService = _SessionManagementService;
241
292
  export {
242
293
  SessionManagementService
243
294
  };
@@ -0,0 +1,74 @@
1
+ import {
2
+ __privateAdd,
3
+ __privateGet,
4
+ __privateMethod,
5
+ __privateSet
6
+ } from "../chunk-7B52C2XE.js";
7
+ var _spans, _pagehideListener, _visibilityListener, _url, _BeaconSpanProcessor_instances, flushViaBeacon_fn;
8
+ import { JsonTraceSerializer } from "@opentelemetry/otlp-transformer";
9
+ const MAX_BUFFER_SIZE = 200;
10
+ class BeaconSpanProcessor {
11
+ constructor(url) {
12
+ __privateAdd(this, _BeaconSpanProcessor_instances);
13
+ __privateAdd(this, _spans, []);
14
+ __privateAdd(this, _pagehideListener);
15
+ __privateAdd(this, _visibilityListener);
16
+ __privateAdd(this, _url);
17
+ __privateSet(this, _url, url);
18
+ if (typeof window !== "undefined") {
19
+ __privateSet(this, _pagehideListener, () => __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this));
20
+ __privateSet(this, _visibilityListener, () => {
21
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
22
+ __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this);
23
+ }
24
+ });
25
+ window.addEventListener("pagehide", __privateGet(this, _pagehideListener));
26
+ if (typeof document !== "undefined") {
27
+ document.addEventListener("visibilitychange", __privateGet(this, _visibilityListener));
28
+ }
29
+ }
30
+ }
31
+ onStart(_span, _parentContext) {
32
+ }
33
+ onEnd(span) {
34
+ __privateGet(this, _spans).push(span);
35
+ if (__privateGet(this, _spans).length > MAX_BUFFER_SIZE) {
36
+ __privateGet(this, _spans).shift();
37
+ }
38
+ }
39
+ forceFlush() {
40
+ return Promise.resolve();
41
+ }
42
+ shutdown() {
43
+ __privateMethod(this, _BeaconSpanProcessor_instances, flushViaBeacon_fn).call(this);
44
+ if (__privateGet(this, _pagehideListener) && typeof window !== "undefined") {
45
+ window.removeEventListener("pagehide", __privateGet(this, _pagehideListener));
46
+ __privateSet(this, _pagehideListener, void 0);
47
+ }
48
+ if (__privateGet(this, _visibilityListener) && typeof document !== "undefined") {
49
+ document.removeEventListener("visibilitychange", __privateGet(this, _visibilityListener));
50
+ __privateSet(this, _visibilityListener, void 0);
51
+ }
52
+ __privateSet(this, _spans, []);
53
+ return Promise.resolve();
54
+ }
55
+ }
56
+ _spans = new WeakMap();
57
+ _pagehideListener = new WeakMap();
58
+ _visibilityListener = new WeakMap();
59
+ _url = new WeakMap();
60
+ _BeaconSpanProcessor_instances = new WeakSet();
61
+ flushViaBeacon_fn = function() {
62
+ if (__privateGet(this, _spans).length === 0) return;
63
+ if (typeof navigator === "undefined" || typeof navigator.sendBeacon !== "function") return;
64
+ try {
65
+ const body = JsonTraceSerializer.serializeRequest(__privateGet(this, _spans));
66
+ const blob = new Blob([body], { type: "application/json" });
67
+ navigator.sendBeacon(__privateGet(this, _url), blob);
68
+ __privateSet(this, _spans, []);
69
+ } catch (e) {
70
+ }
71
+ };
72
+ export {
73
+ BeaconSpanProcessor
74
+ };
@@ -2,7 +2,11 @@ import {
2
2
  __async,
3
3
  __spreadValues
4
4
  } from "../chunk-7B52C2XE.js";
5
- import { BatchSpanProcessor, BasicTracerProvider, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
5
+ import {
6
+ BatchSpanProcessor,
7
+ BasicTracerProvider,
8
+ TraceIdRatioBasedSampler
9
+ } from "@opentelemetry/sdk-trace-base";
6
10
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
7
11
  import { resourceFromAttributes } from "@opentelemetry/resources";
8
12
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
@@ -33,21 +37,29 @@ function initTelemetry(opts) {
33
37
  } catch (e) {
34
38
  }
35
39
  }
36
- const resource = resourceFromAttributes(__spreadValues(__spreadValues({
40
+ const resource = resourceFromAttributes(__spreadValues(__spreadValues(__spreadValues({
37
41
  [ATTR_SERVICE_NAME]: opts.isPortal ? "para-portal" : "para-sdk",
38
42
  [ATTR_SERVICE_VERSION]: opts.sdkVersion,
39
43
  "para.platform": opts.sdkType
40
- }, opts.partnerId ? { "para.partner_id": opts.partnerId } : {}), opts.resourceAttributes));
44
+ }, opts.clientType ? { "para.client_type": opts.clientType } : {}), opts.partnerId ? { "para.partner_id": opts.partnerId } : {}), opts.resourceAttributes));
41
45
  const exporter = new OTLPTraceExporter({
42
46
  url: opts.tunnelUrl
43
47
  // No auth header — the tunnel is a dumb proxy. Per-IP rate limit + body cap on the
44
48
  // BE side keep abuse contained.
45
49
  });
46
50
  const sampler = new TraceIdRatioBasedSampler(Math.max(0, Math.min(1, opts.sampleRate)));
51
+ const spanProcessors = [new UxStateSpanProcessor(), new BatchSpanProcessor(exporter)];
52
+ if (isWeb && opts.isPortal) {
53
+ try {
54
+ const { BeaconSpanProcessor } = yield import("./BeaconSpanProcessor.js");
55
+ spanProcessors.push(new BeaconSpanProcessor(opts.tunnelUrl));
56
+ } catch (e) {
57
+ }
58
+ }
47
59
  tracerProvider = new BasicTracerProvider({
48
60
  resource,
49
61
  sampler,
50
- spanProcessors: [new UxStateSpanProcessor(), new BatchSpanProcessor(exporter)]
62
+ spanProcessors
51
63
  });
52
64
  trace.setGlobalTracerProvider(tracerProvider);
53
65
  propagation.setGlobalPropagator(
@@ -295,6 +295,7 @@ export declare abstract class ParaCore implements CoreInterface {
295
295
  onRampPurchase: OnRampPurchase;
296
296
  } | undefined;
297
297
  protected platformUtils: PlatformUtils;
298
+ private clientType?;
298
299
  protected nonPersistedStorageKeys: string[];
299
300
  private localStorageGetItem;
300
301
  private localStorageSetItem;
@@ -3,11 +3,21 @@ import { Environment } from '../types/index.js';
3
3
  export declare function getBaseOAuthUrl(env: Environment): string;
4
4
  export declare function getBaseUrl(env: Environment, scheme?: 'http' | 'ws'): string;
5
5
  export declare function getBaseMPCNetworkUrl(env: Environment, useWebsocket?: boolean): string;
6
- export declare function initClient({ env, version, apiKey, partnerId, useFetchAdapter, retrieveSessionCookie, persistSessionCookie, staticTraceContext, }: {
6
+ /**
7
+ * Maps a raw host-platform / sdkType value onto the normalized `client_type`
8
+ * dimension used by backend analytics + Sentry. Unknown values (e.g. BRIDGE
9
+ * without a resolved host) return undefined so the backend omits the property
10
+ * rather than emitting a meaningless bucket. CLI sets `client_type` directly
11
+ * outside this core SDK path.
12
+ */
13
+ export declare function normalizePlatform(platform?: string): string | undefined;
14
+ export declare function initClient({ env, version, apiKey, partnerId, platform, useFetchAdapter, retrieveSessionCookie, persistSessionCookie, staticTraceContext, }: {
7
15
  env: Environment;
8
16
  version?: string;
9
17
  apiKey: string;
10
18
  partnerId?: string;
19
+ /** Raw host platform (e.g. 'iOS', 'flutter') or sdkType; normalized to client_type. */
20
+ platform?: string;
11
21
  useFetchAdapter?: boolean;
12
22
  retrieveSessionCookie?: () => string;
13
23
  persistSessionCookie?: (cookie: string) => void;
@@ -0,0 +1,10 @@
1
+ import type { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';
2
+ import type { Context } from '@opentelemetry/api';
3
+ export declare class BeaconSpanProcessor implements SpanProcessor {
4
+ #private;
5
+ constructor(url: string);
6
+ onStart(_span: Span, _parentContext: Context): void;
7
+ onEnd(span: ReadableSpan): void;
8
+ forceFlush(): Promise<void>;
9
+ shutdown(): Promise<void>;
10
+ }
@@ -4,6 +4,7 @@ export type InitTelemetryOpts = {
4
4
  tunnelUrl: string;
5
5
  sampleRate: number;
6
6
  sdkType: SDKType;
7
+ clientType?: string;
7
8
  sdkVersion: string;
8
9
  partnerId?: string;
9
10
  resourceAttributes?: Attributes;
@@ -59,6 +59,14 @@ export type SupportedWalletTypeConfig = {
59
59
  optional?: boolean;
60
60
  };
61
61
  export interface ConstructorOpts {
62
+ /**
63
+ * Host platform of the native shell hosting this SDK instance (e.g. 'iOS',
64
+ * 'flutter'). Set by bridge-v2 from the native Para#init metadata so the
65
+ * backend can attribute analytics/errors to the originating SDK. Normalized
66
+ * to client_type (swift | flutter | react-native | web). React Native and
67
+ * web derive their platform from platformUtils.sdkType and leave this unset.
68
+ */
69
+ hostPlatform?: string;
62
70
  externalWalletConnectionOnly?: boolean;
63
71
  useStorageOverrides?: boolean;
64
72
  disableWorkers?: boolean;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@getpara/core-sdk",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "dependencies": {
5
5
  "@celo/utils": "^8.0.2",
6
6
  "@cosmjs/encoding": "^0.32.4",
7
7
  "@ethereumjs/util": "^9.1.0",
8
- "@getpara/user-management-client": "3.0.0",
8
+ "@getpara/user-management-client": "3.2.0",
9
9
  "@noble/hashes": "^1.5.0",
10
10
  "@opentelemetry/api": "^1.9.1",
11
11
  "@opentelemetry/context-zone": "^2.7.1",
@@ -14,6 +14,7 @@
14
14
  "@opentelemetry/instrumentation": "^0.215.0",
15
15
  "@opentelemetry/instrumentation-fetch": "^0.215.0",
16
16
  "@opentelemetry/instrumentation-xml-http-request": "^0.216.0",
17
+ "@opentelemetry/otlp-transformer": "^0.215.0",
17
18
  "@opentelemetry/resources": "^2.7.0",
18
19
  "@opentelemetry/sdk-trace-base": "^2.7.0",
19
20
  "@opentelemetry/semantic-conventions": "^1.40.0",
@@ -40,7 +41,7 @@
40
41
  "dist",
41
42
  "package.json"
42
43
  ],
43
- "gitHead": "e6c42e42adfcfadb0114b1ac65ba5203e6274712",
44
+ "gitHead": "096152f48e9d9a64fdb192588315c64d33d15a1b",
44
45
  "main": "dist/cjs/index.js",
45
46
  "module": "dist/esm/index.js",
46
47
  "scripts": {