@meshconnect/uwc-core 1.0.0 → 1.0.1-snapshot.c7e65ce

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.
Files changed (76) hide show
  1. package/README.md +211 -66
  2. package/dist/events.d.ts +69 -2
  3. package/dist/events.d.ts.map +1 -1
  4. package/dist/events.js +40 -1
  5. package/dist/events.js.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/logger/create-logger.d.ts +32 -0
  11. package/dist/logger/create-logger.d.ts.map +1 -0
  12. package/dist/logger/create-logger.js +69 -0
  13. package/dist/logger/create-logger.js.map +1 -0
  14. package/dist/managers/event-manager.d.ts.map +1 -1
  15. package/dist/managers/event-manager.js +5 -1
  16. package/dist/managers/event-manager.js.map +1 -1
  17. package/dist/observability/connect-observer.d.ts +22 -0
  18. package/dist/observability/connect-observer.d.ts.map +1 -0
  19. package/dist/observability/connect-observer.js +60 -0
  20. package/dist/observability/connect-observer.js.map +1 -0
  21. package/dist/observability/instrument-handoff.d.ts +39 -0
  22. package/dist/observability/instrument-handoff.d.ts.map +1 -0
  23. package/dist/observability/instrument-handoff.js +86 -0
  24. package/dist/observability/instrument-handoff.js.map +1 -0
  25. package/dist/observability/scrub.d.ts +30 -0
  26. package/dist/observability/scrub.d.ts.map +1 -0
  27. package/dist/observability/scrub.js +135 -0
  28. package/dist/observability/scrub.js.map +1 -0
  29. package/dist/observability/telemetry.d.ts +77 -0
  30. package/dist/observability/telemetry.d.ts.map +1 -0
  31. package/dist/observability/telemetry.js +138 -0
  32. package/dist/observability/telemetry.js.map +1 -0
  33. package/dist/services/connection-service.d.ts +2 -1
  34. package/dist/services/connection-service.d.ts.map +1 -1
  35. package/dist/services/connection-service.js +35 -15
  36. package/dist/services/connection-service.js.map +1 -1
  37. package/dist/services/network-switch-service.d.ts +2 -1
  38. package/dist/services/network-switch-service.d.ts.map +1 -1
  39. package/dist/services/network-switch-service.js +33 -15
  40. package/dist/services/network-switch-service.js.map +1 -1
  41. package/dist/universal-wallet-connector.d.ts +51 -2
  42. package/dist/universal-wallet-connector.d.ts.map +1 -1
  43. package/dist/universal-wallet-connector.js +163 -61
  44. package/dist/universal-wallet-connector.js.map +1 -1
  45. package/dist/utils/id.d.ts +2 -0
  46. package/dist/utils/id.d.ts.map +1 -0
  47. package/dist/utils/id.js +41 -0
  48. package/dist/utils/id.js.map +1 -0
  49. package/dist/utils/to-wallet-error.d.ts +16 -0
  50. package/dist/utils/to-wallet-error.d.ts.map +1 -0
  51. package/dist/utils/to-wallet-error.js +33 -0
  52. package/dist/utils/to-wallet-error.js.map +1 -0
  53. package/package.json +5 -5
  54. package/src/events.ts +114 -1
  55. package/src/index.ts +6 -0
  56. package/src/logger/create-logger.test.ts +111 -0
  57. package/src/logger/create-logger.ts +101 -0
  58. package/src/managers/event-manager.test.ts +25 -0
  59. package/src/managers/event-manager.ts +5 -1
  60. package/src/observability/connect-observer.test.ts +100 -0
  61. package/src/observability/connect-observer.ts +71 -0
  62. package/src/observability/instrument-handoff.test.ts +150 -0
  63. package/src/observability/instrument-handoff.ts +122 -0
  64. package/src/observability/scrub.test.ts +109 -0
  65. package/src/observability/scrub.ts +142 -0
  66. package/src/observability/telemetry.test.ts +134 -0
  67. package/src/observability/telemetry.ts +211 -0
  68. package/src/services/connection-service.test.ts +66 -1
  69. package/src/services/connection-service.ts +54 -32
  70. package/src/services/network-switch-service.test.ts +51 -1
  71. package/src/services/network-switch-service.ts +43 -23
  72. package/src/universal-wallet-connector.observability.test.ts +265 -0
  73. package/src/universal-wallet-connector.ts +244 -71
  74. package/src/utils/id.test.ts +15 -0
  75. package/src/utils/id.ts +48 -0
  76. package/src/utils/to-wallet-error.ts +36 -0
@@ -0,0 +1,69 @@
1
+ const LEVEL_WEIGHT = {
2
+ debug: 0,
3
+ info: 1,
4
+ warn: 2,
5
+ error: 3
6
+ };
7
+ /**
8
+ * Resolve the effective console threshold, honouring a runtime global override so
9
+ * an issue can be triaged in a wallet's in-app browser WITHOUT a redeploy:
10
+ *
11
+ * window.UWC_DEBUG = true // surface everything (debug)
12
+ * window.UWC_DEBUG = 'info' // surface info and above
13
+ *
14
+ * The override only ever LOWERS the threshold (shows more) — it can't hide logs a
15
+ * consumer explicitly opted into. Re-read on every call so toggling it mid-session
16
+ * takes effect immediately. SSR/Node-safe via the `globalThis` guard.
17
+ */
18
+ function resolveWeight(configured) {
19
+ if (typeof globalThis !== 'undefined') {
20
+ const override = globalThis.UWC_DEBUG;
21
+ if (override === true)
22
+ return LEVEL_WEIGHT.debug;
23
+ if (typeof override === 'string' && override in LEVEL_WEIGHT) {
24
+ return Math.min(LEVEL_WEIGHT[override], LEVEL_WEIGHT[configured]);
25
+ }
26
+ }
27
+ return LEVEL_WEIGHT[configured];
28
+ }
29
+ /**
30
+ * Console-backed, level-gated logger. ~zero-dep. Two seams:
31
+ * - `onLog` fires pre-gate (all levels) → drives the observer.
32
+ * - the console sink fires only at/above the threshold.
33
+ */
34
+ export function createLogger(options = {}) {
35
+ let configured = options.level ?? 'warn';
36
+ const customSink = options.sink;
37
+ const emit = (level, message, args) => {
38
+ // Pre-gate tap first — observability must not depend on the console threshold.
39
+ if (options.onLog) {
40
+ try {
41
+ options.onLog(level, message, args);
42
+ }
43
+ catch {
44
+ // A failing sink must never break the caller's log statement.
45
+ }
46
+ }
47
+ if (LEVEL_WEIGHT[level] < resolveWeight(configured))
48
+ return;
49
+ if (customSink) {
50
+ customSink[level](message, ...args);
51
+ }
52
+ else {
53
+ const line = `[${new Date().toISOString()}] ${level.toUpperCase()}: ${message}`;
54
+ // eslint-disable-next-line no-console -- this IS the default console sink
55
+ console[level](line, ...args);
56
+ }
57
+ };
58
+ return {
59
+ debug: (message, ...args) => emit('debug', message, args),
60
+ info: (message, ...args) => emit('info', message, args),
61
+ warn: (message, ...args) => emit('warn', message, args),
62
+ error: (message, ...args) => emit('error', message, args),
63
+ setLevel: level => {
64
+ configured = level;
65
+ },
66
+ getLevel: () => configured
67
+ };
68
+ }
69
+ //# sourceMappingURL=create-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-logger.js","sourceRoot":"","sources":["../../src/logger/create-logger.ts"],"names":[],"mappings":"AAEA,MAAM,YAAY,GAA6B;IAC7C,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAA;AA8BD;;;;;;;;;;GAUG;AACH,SAAS,aAAa,CAAC,UAAoB;IACzC,IAAI,OAAO,UAAU,KAAK,WAAW,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAI,UAAsC,CAAC,SAAS,CAAA;QAClE,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,YAAY,CAAC,KAAK,CAAA;QAChD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,IAAI,YAAY,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC,GAAG,CACb,YAAY,CAAC,QAAoB,CAAC,EAClC,YAAY,CAAC,UAAU,CAAC,CACzB,CAAA;QACH,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC,UAAU,CAAC,CAAA;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC5D,IAAI,UAAU,GAAa,OAAO,CAAC,KAAK,IAAI,MAAM,CAAA;IAClD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAA;IAE/B,MAAM,IAAI,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAe,EAAQ,EAAE;QACvE,+EAA+E;QAC/E,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;QACH,CAAC;QACD,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC;YAAE,OAAM;QAC3D,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAA;YAC/E,0EAA0E;YAC1E,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;QACzD,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;QACvD,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;QACvD,KAAK,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;QACzD,QAAQ,EAAE,KAAK,CAAC,EAAE;YAChB,UAAU,GAAG,KAAK,CAAA;QACpB,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;KAC3B,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-manager.d.ts","sourceRoot":"","sources":["../../src/managers/event-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAE5E,KAAK,cAAc,GAAG,MAAM,IAAI,CAAA;AAGhC;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAqD;IAEtE,mEAAmE;IACnE,EAAE,CAAC,CAAC,SAAS,YAAY,EACvB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,MAAM,IAAI;IAUb,wEAAwE;IACxE,IAAI,CAAC,CAAC,SAAS,YAAY,EACzB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,MAAM,IAAI;IAQb,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAK1E,IAAI,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAOlE,OAAO,CAAC,QAAQ;IAkBhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;IAI/C,kFAAkF;IAClF,MAAM,IAAI,IAAI;IAId,gBAAgB,IAAI,MAAM;IAQ1B,iBAAiB,IAAI,IAAI;CAG1B"}
1
+ {"version":3,"file":"event-manager.d.ts","sourceRoot":"","sources":["../../src/managers/event-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAG5E,KAAK,cAAc,GAAG,MAAM,IAAI,CAAA;AAGhC;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,SAAS,CAAqD;IAEtE,mEAAmE;IACnE,EAAE,CAAC,CAAC,SAAS,YAAY,EACvB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,MAAM,IAAI;IAUb,wEAAwE;IACxE,IAAI,CAAC,CAAC,SAAS,YAAY,EACzB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC5B,MAAM,IAAI;IAQb,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAK1E,IAAI,CAAC,CAAC,SAAS,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAUlE,OAAO,CAAC,QAAQ;IAkBhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;IAI/C,kFAAkF;IAClF,MAAM,IAAI,IAAI;IAId,gBAAgB,IAAI,MAAM;IAQ1B,iBAAiB,IAAI,IAAI;CAG1B"}
@@ -1,3 +1,4 @@
1
+ import { TELEMETRY_ONLY_EVENTS } from '../events';
1
2
  /**
2
3
  * Typed event emitter used by UniversalWalletConnector.
3
4
  *
@@ -31,7 +32,10 @@ export class EventManager {
31
32
  }
32
33
  emit(event, data) {
33
34
  this.dispatch(event, data);
34
- if (event !== 'change') {
35
+ // Telemetry-only events (logs, handoff instrumentation) describe what
36
+ // happened, not a state change — don't cascade them to `change`, or every
37
+ // log line would trigger a React re-render via legacy `subscribe()`.
38
+ if (event !== 'change' && !TELEMETRY_ONLY_EVENTS.has(event)) {
35
39
  this.dispatch('change', undefined);
36
40
  }
37
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"event-manager.js","sourceRoot":"","sources":["../../src/managers/event-manager.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACf,SAAS,GAA4C,IAAI,GAAG,EAAE,CAAA;IAEtE,mEAAmE;IACnE,EAAE,CACA,KAAQ,EACR,QAA6B;QAE7B,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAChC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAA2B,CAAC,CAAA;QACpC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IACxC,CAAC;IAED,wEAAwE;IACxE,IAAI,CACF,KAAQ,EACR,QAA6B;QAE7B,MAAM,OAAO,GAAwB,IAAI,CAAC,EAAE;YAC1C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YACxB,QAAQ,CAAC,IAAI,CAAC,CAAA;QAChB,CAAC,CAAA;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAChC,CAAC;IAED,GAAG,CAAyB,KAAQ,EAAE,QAA6B;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrC,GAAG,EAAE,MAAM,CAAC,QAA2B,CAAC,CAAA;IAC1C,CAAC;IAED,IAAI,CAAyB,KAAQ,EAAE,IAAoB;QACzD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1B,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAkC,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IAEO,QAAQ,CACd,KAAQ,EACR,IAAoB;QAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,OAAM;QAClC,mDAAmD;QACnD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAA;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,iEAAiE;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAED,oEAAoE;IAEpE;;;OAGG;IACH,SAAS,CAAC,QAAwB;QAChC,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACpC,CAAC;IAED,kFAAkF;IAClF,MAAM;QACJ,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAkC,CAAC,CAAA;IAC7D,CAAC;IAED,gBAAgB;QACd,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,KAAK,IAAI,GAAG,CAAC,IAAI,CAAA;QACnB,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF"}
1
+ {"version":3,"file":"event-manager.js","sourceRoot":"","sources":["../../src/managers/event-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AAKjD;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACf,SAAS,GAA4C,IAAI,GAAG,EAAE,CAAA;IAEtE,mEAAmE;IACnE,EAAE,CACA,KAAQ,EACR,QAA6B;QAE7B,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAA;YACf,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAChC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,QAA2B,CAAC,CAAA;QACpC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IACxC,CAAC;IAED,wEAAwE;IACxE,IAAI,CACF,KAAQ,EACR,QAA6B;QAE7B,MAAM,OAAO,GAAwB,IAAI,CAAC,EAAE;YAC1C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YACxB,QAAQ,CAAC,IAAI,CAAC,CAAA;QAChB,CAAC,CAAA;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAChC,CAAC;IAED,GAAG,CAAyB,KAAQ,EAAE,QAA6B;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrC,GAAG,EAAE,MAAM,CAAC,QAA2B,CAAC,CAAA;IAC1C,CAAC;IAED,IAAI,CAAyB,KAAQ,EAAE,IAAoB;QACzD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAC1B,sEAAsE;QACtE,0EAA0E;QAC1E,qEAAqE;QACrE,IAAI,KAAK,KAAK,QAAQ,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5D,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAkC,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IAEO,QAAQ,CACd,KAAQ,EACR,IAAoB;QAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACrC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,OAAM;QAClC,mDAAmD;QACnD,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAA;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,iEAAiE;YACnE,CAAC;QACH,CAAC;IACH,CAAC;IAED,oEAAoE;IAEpE;;;OAGG;IACH,SAAS,CAAC,QAAwB;QAChC,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACpC,CAAC;IAED,kFAAkF;IAClF,MAAM;QACJ,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAkC,CAAC,CAAA;IAC7D,CAAC;IAED,gBAAgB;QACd,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,KAAK,IAAI,GAAG,CAAC,IAAI,CAAA;QACnB,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import type { EventManager } from '../managers/event-manager';
2
+ import { type UWCObserver } from './telemetry';
3
+ /**
4
+ * Bridge the internal event bus to a consumer-supplied `UWCObserver`.
5
+ *
6
+ * Subscribes to every `UWCEventMap` event, normalizes each to a PII-safe
7
+ * `UWCTelemetryEvent`, and forwards it to `observer.onEvent`. The `log` event is
8
+ * routed to `observer.onLog` instead (with scrubbed args) — it's the logging
9
+ * channel, not a telemetry record. `change` is skipped: it's the legacy
10
+ * re-render cascade and carries no payload.
11
+ *
12
+ * Returns a teardown function that removes every subscription. The caller MUST
13
+ * invoke it on connector teardown — leaving these attached is the same listener-
14
+ * leak class as the historical `BridgeParent.destroy()` zombie bug.
15
+ *
16
+ * @param getCorrelationId - resolves the correlation id stamped on every event,
17
+ * called per-event so a consumer-supplied id that arrives/changes after
18
+ * construction is honoured. Must not throw (the caller pre-wraps it).
19
+ * @param now - clock injectable for deterministic tests; defaults to `Date.now`.
20
+ */
21
+ export declare function connectObserver(eventManager: EventManager, observer: UWCObserver, getCorrelationId: () => string, now?: () => number): () => void;
22
+ //# sourceMappingURL=connect-observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-observer.d.ts","sourceRoot":"","sources":["../../src/observability/connect-observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAG7D,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,WAAW,EACrB,gBAAgB,EAAE,MAAM,MAAM,EAC9B,GAAG,GAAE,MAAM,MAAiB,GAC3B,MAAM,IAAI,CAyCZ"}
@@ -0,0 +1,60 @@
1
+ import { UWC_EVENT_NAMES } from '../events';
2
+ import { normalizeEvent } from './telemetry';
3
+ import { scrubArgs } from './scrub';
4
+ /**
5
+ * Bridge the internal event bus to a consumer-supplied `UWCObserver`.
6
+ *
7
+ * Subscribes to every `UWCEventMap` event, normalizes each to a PII-safe
8
+ * `UWCTelemetryEvent`, and forwards it to `observer.onEvent`. The `log` event is
9
+ * routed to `observer.onLog` instead (with scrubbed args) — it's the logging
10
+ * channel, not a telemetry record. `change` is skipped: it's the legacy
11
+ * re-render cascade and carries no payload.
12
+ *
13
+ * Returns a teardown function that removes every subscription. The caller MUST
14
+ * invoke it on connector teardown — leaving these attached is the same listener-
15
+ * leak class as the historical `BridgeParent.destroy()` zombie bug.
16
+ *
17
+ * @param getCorrelationId - resolves the correlation id stamped on every event,
18
+ * called per-event so a consumer-supplied id that arrives/changes after
19
+ * construction is honoured. Must not throw (the caller pre-wraps it).
20
+ * @param now - clock injectable for deterministic tests; defaults to `Date.now`.
21
+ */
22
+ export function connectObserver(eventManager, observer, getCorrelationId, now = Date.now) {
23
+ const unsubscribers = [];
24
+ for (const name of UWC_EVENT_NAMES) {
25
+ if (name === 'change')
26
+ continue;
27
+ if (name === 'log') {
28
+ unsubscribers.push(eventManager.on('log', data => {
29
+ if (!observer.onLog)
30
+ return;
31
+ const { level, message, args } = data;
32
+ try {
33
+ observer.onLog(level, message, scrubArgs(args));
34
+ }
35
+ catch {
36
+ // A throwing observer must not break the dispatch chain.
37
+ }
38
+ }));
39
+ continue;
40
+ }
41
+ unsubscribers.push(eventManager.on(name, data => {
42
+ if (!observer.onEvent)
43
+ return;
44
+ try {
45
+ // Normalize OUTSIDE the EventManager's own try/catch (which guards the
46
+ // listener, not the work the listener does), then hand off.
47
+ observer.onEvent(normalizeEvent(name, data, getCorrelationId(), now()));
48
+ }
49
+ catch {
50
+ // Same: never let a bad observer break other listeners.
51
+ }
52
+ }));
53
+ }
54
+ return () => {
55
+ for (const unsubscribe of unsubscribers)
56
+ unsubscribe();
57
+ unsubscribers.length = 0;
58
+ };
59
+ }
60
+ //# sourceMappingURL=connect-observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-observer.js","sourceRoot":"","sources":["../../src/observability/connect-observer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAC7B,YAA0B,EAC1B,QAAqB,EACrB,gBAA8B,EAC9B,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,aAAa,GAAsB,EAAE,CAAA;IAE3C,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;QACnC,IAAI,IAAI,KAAK,QAAQ;YAAE,SAAQ;QAE/B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,aAAa,CAAC,IAAI,CAChB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE;gBAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK;oBAAE,OAAM;gBAC3B,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAA0B,CAAA;gBAC3D,IAAI,CAAC;oBACH,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;gBACjD,CAAC;gBAAC,MAAM,CAAC;oBACP,yDAAyD;gBAC3D,CAAC;YACH,CAAC,CAAC,CACH,CAAA;YACD,SAAQ;QACV,CAAC;QAED,aAAa,CAAC,IAAI,CAChB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE;YAC3B,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,OAAM;YAC7B,IAAI,CAAC;gBACH,uEAAuE;gBACvE,4DAA4D;gBAC5D,QAAQ,CAAC,OAAO,CACd,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,GAAG,EAAE,CAAC,CACtD,CAAA;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;QACH,CAAC,CAAC,CACH,CAAA;IACH,CAAC;IAED,OAAO,GAAG,EAAE;QACV,KAAK,MAAM,WAAW,IAAI,aAAa;YAAE,WAAW,EAAE,CAAA;QACtD,aAAa,CAAC,MAAM,GAAG,CAAC,CAAA;IAC1B,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { ConnectionMode, Namespace, NetworkId } from '@meshconnect/uwc-types';
2
+ import type { EventManager } from '../managers/event-manager';
3
+ import type { UWCOperation } from '../events';
4
+ /** Routing facets known at the moment of a wallet handoff. No address, no payload. */
5
+ export interface HandoffContext {
6
+ operation: UWCOperation;
7
+ connectionMode: ConnectionMode;
8
+ walletId?: string | undefined;
9
+ namespace?: Namespace | undefined;
10
+ chainId?: NetworkId | undefined;
11
+ }
12
+ export interface InstrumentHandoffOptions {
13
+ eventManager: EventManager;
14
+ /**
15
+ * Deadline after which `walletTimedOut` fires as a SIDE marker (never rejects).
16
+ * `0` disables the marker. `tonConnect` is always skipped — TON bounds its own
17
+ * wallet response internally, so a second observational timer would be noise.
18
+ */
19
+ timeoutMs: number;
20
+ /** Clock injectable for deterministic tests. */
21
+ now?: () => number;
22
+ }
23
+ /**
24
+ * Instrument one wallet-boundary op so the blind window is measured.
25
+ *
26
+ * Emits `awaitingWallet` immediately (before the await that hands control to the
27
+ * wallet), then exactly one terminal — `walletSucceeded` (success),
28
+ * `walletRejected` (explicit user no), or `walletFailed` (any other error) — all
29
+ * carrying the same `handoffId` and a `durationMs`. An intentional abort emits no
30
+ * terminal (the caller cancelled; it isn't a wallet outcome).
31
+ *
32
+ * A non-cancelling `walletTimedOut` marker fires if the deadline passes while the
33
+ * op is still pending; it is cleared the instant the op settles, so a fast op
34
+ * never false-fires it, and it NEVER rejects/aborts/mutates state — the wrapped
35
+ * promise stays the single terminal source of truth. This is a deliberate
36
+ * deviation from TON's cancelling `withWalletResponseTimeout`.
37
+ */
38
+ export declare function instrumentHandoff<T>(options: InstrumentHandoffOptions, context: HandoffContext, fn: () => Promise<T>): Promise<T>;
39
+ //# sourceMappingURL=instrument-handoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-handoff.d.ts","sourceRoot":"","sources":["../../src/observability/instrument-handoff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAS7C,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,YAAY,CAAA;IACvB,cAAc,EAAE,cAAc,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACjC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAChC;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,YAAY,CAAA;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,OAAO,EAAE,wBAAwB,EACjC,OAAO,EAAE,cAAc,EACvB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAkEZ"}
@@ -0,0 +1,86 @@
1
+ import { generateId } from '../utils/id';
2
+ import { isAbortError, isUserRejection, toWalletError } from '../utils/to-wallet-error';
3
+ import { derivePlatform, deriveWalletFlowType } from './telemetry';
4
+ /**
5
+ * Instrument one wallet-boundary op so the blind window is measured.
6
+ *
7
+ * Emits `awaitingWallet` immediately (before the await that hands control to the
8
+ * wallet), then exactly one terminal — `walletSucceeded` (success),
9
+ * `walletRejected` (explicit user no), or `walletFailed` (any other error) — all
10
+ * carrying the same `handoffId` and a `durationMs`. An intentional abort emits no
11
+ * terminal (the caller cancelled; it isn't a wallet outcome).
12
+ *
13
+ * A non-cancelling `walletTimedOut` marker fires if the deadline passes while the
14
+ * op is still pending; it is cleared the instant the op settles, so a fast op
15
+ * never false-fires it, and it NEVER rejects/aborts/mutates state — the wrapped
16
+ * promise stays the single terminal source of truth. This is a deliberate
17
+ * deviation from TON's cancelling `withWalletResponseTimeout`.
18
+ */
19
+ export async function instrumentHandoff(options, context, fn) {
20
+ const { eventManager, timeoutMs } = options;
21
+ const now = options.now ?? Date.now;
22
+ const handoffId = generateId();
23
+ const platform = derivePlatform();
24
+ const start = now();
25
+ eventManager.emit('awaitingWallet', {
26
+ operation: context.operation,
27
+ connectionMode: context.connectionMode,
28
+ walletId: context.walletId,
29
+ namespace: context.namespace,
30
+ chainId: context.chainId,
31
+ handoffId,
32
+ walletFlowType: deriveWalletFlowType(context.connectionMode, platform),
33
+ platform,
34
+ timestamp: start
35
+ });
36
+ const shouldTime = timeoutMs > 0 && context.connectionMode !== 'tonConnect';
37
+ const timer = shouldTime
38
+ ? setTimeout(() => {
39
+ eventManager.emit('walletTimedOut', {
40
+ handoffId,
41
+ operation: context.operation,
42
+ durationMs: now() - start
43
+ });
44
+ }, timeoutMs)
45
+ : undefined;
46
+ try {
47
+ const result = await fn();
48
+ eventManager.emit('walletSucceeded', {
49
+ handoffId,
50
+ operation: context.operation,
51
+ durationMs: now() - start
52
+ });
53
+ return result;
54
+ }
55
+ catch (error) {
56
+ // Caller-driven cancellation isn't a wallet outcome — leave it unterminated,
57
+ // mirroring the façade's `emitError` AbortError skip. Checked BEFORE rejection
58
+ // is safe: real user rejections surface as WalletConnectorError{rejected} or
59
+ // EIP-1193 4001 (see isUserRejection), never as an AbortError — within UWC's
60
+ // dependency surface AbortError is produced only by the caller's AbortSignal.
61
+ if (isAbortError(error))
62
+ throw error;
63
+ const durationMs = now() - start;
64
+ if (isUserRejection(error)) {
65
+ eventManager.emit('walletRejected', {
66
+ handoffId,
67
+ operation: context.operation,
68
+ durationMs
69
+ });
70
+ }
71
+ else {
72
+ eventManager.emit('walletFailed', {
73
+ handoffId,
74
+ operation: context.operation,
75
+ durationMs,
76
+ error: toWalletError(error)
77
+ });
78
+ }
79
+ throw error;
80
+ }
81
+ finally {
82
+ if (timer)
83
+ clearTimeout(timer);
84
+ }
85
+ }
86
+ //# sourceMappingURL=instrument-handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrument-handoff.js","sourceRoot":"","sources":["../../src/observability/instrument-handoff.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAuBlE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAiC,EACjC,OAAuB,EACvB,EAAoB;IAEpB,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IAEnC,MAAM,SAAS,GAAG,UAAU,EAAE,CAAA;IAC9B,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAA;IACjC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAA;IAEnB,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE;QAClC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS;QACT,cAAc,EAAE,oBAAoB,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC;QACtE,QAAQ;QACR,SAAS,EAAE,KAAK;KACjB,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,IAAI,OAAO,CAAC,cAAc,KAAK,YAAY,CAAA;IAC3E,MAAM,KAAK,GAA8C,UAAU;QACjE,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE;YACd,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE;gBAClC,SAAS;gBACT,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK;aAC1B,CAAC,CAAA;QACJ,CAAC,EAAE,SAAS,CAAC;QACf,CAAC,CAAC,SAAS,CAAA;IAEb,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;QACzB,YAAY,CAAC,IAAI,CAAC,iBAAiB,EAAE;YACnC,SAAS;YACT,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,GAAG,EAAE,GAAG,KAAK;SAC1B,CAAC,CAAA;QACF,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,6EAA6E;QAC7E,+EAA+E;QAC/E,6EAA6E;QAC7E,6EAA6E;QAC7E,8EAA8E;QAC9E,IAAI,YAAY,CAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAA;QAEpC,MAAM,UAAU,GAAG,GAAG,EAAE,GAAG,KAAK,CAAA;QAChC,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,YAAY,CAAC,IAAI,CAAC,gBAAgB,EAAE;gBAClC,SAAS;gBACT,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,UAAU;aACX,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE;gBAChC,SAAS;gBACT,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,UAAU;gBACV,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAChC,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * PII / secret scrubber for data leaving UWC through the observer seam.
3
+ *
4
+ * Security-first: the normalized `UWCTelemetryEvent` excludes signing payloads
5
+ * and addresses by construction — its STRUCTURED fields have no slot for them
6
+ * (§ telemetry.ts). This scrubber is the second line of defence for the two
7
+ * surfaces that CAN still carry free-form data on the observer path: the
8
+ * `WalletError.message` forwarded to `observer.onEvent`, and the `args` forwarded
9
+ * to `observer.onLog`. (The console / custom-logger sink receives raw data — it's
10
+ * local, not egress.)
11
+ *
12
+ * Two detection layers:
13
+ * - KEY-based redaction of sensitive object keys — substring match (not regex),
14
+ * avoiding the ReDoS class that Wiz flags on user-input regexes.
15
+ * - CONTENT-based redaction of secrets embedded in free-form strings (hex blobs,
16
+ * secret k/v params, bearer tokens) — via linear, single-quantifier regexes
17
+ * (see SECRET_CONTENT_PATTERNS) that carry no catastrophic-backtracking risk.
18
+ */
19
+ /**
20
+ * Recursively copy `value`, redacting sensitive object-keys, capping long
21
+ * strings, and rendering non-serializable values inert. The returned objects use
22
+ * a null prototype so a malicious `__proto__`/`constructor` key in attacker-shaped
23
+ * input can't pollute `Object.prototype` downstream.
24
+ */
25
+ export declare function scrubValue(value: unknown, depth?: number): unknown;
26
+ /** Scrub a free-form error message (string in → capped string out). */
27
+ export declare function scrubMessage(message: string): string;
28
+ /** Scrub a variadic `args` array before it reaches `observer.onLog`. */
29
+ export declare function scrubArgs(args: unknown[]): unknown[];
30
+ //# sourceMappingURL=scrub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.d.ts","sourceRoot":"","sources":["../../src/observability/scrub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAyEH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAmC7D;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAEpD"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * PII / secret scrubber for data leaving UWC through the observer seam.
3
+ *
4
+ * Security-first: the normalized `UWCTelemetryEvent` excludes signing payloads
5
+ * and addresses by construction — its STRUCTURED fields have no slot for them
6
+ * (§ telemetry.ts). This scrubber is the second line of defence for the two
7
+ * surfaces that CAN still carry free-form data on the observer path: the
8
+ * `WalletError.message` forwarded to `observer.onEvent`, and the `args` forwarded
9
+ * to `observer.onLog`. (The console / custom-logger sink receives raw data — it's
10
+ * local, not egress.)
11
+ *
12
+ * Two detection layers:
13
+ * - KEY-based redaction of sensitive object keys — substring match (not regex),
14
+ * avoiding the ReDoS class that Wiz flags on user-input regexes.
15
+ * - CONTENT-based redaction of secrets embedded in free-form strings (hex blobs,
16
+ * secret k/v params, bearer tokens) — via linear, single-quantifier regexes
17
+ * (see SECRET_CONTENT_PATTERNS) that carry no catastrophic-backtracking risk.
18
+ */
19
+ /**
20
+ * Separator-free, lower-cased fragments that mark a value as sensitive. Keys are
21
+ * normalized (separators stripped) before matching, so `walletPrivateKey`,
22
+ * `private_key`, `x-api-key`, and `authToken` all hit the same short list. This
23
+ * over-redacts by design (a `security`-first scrubber prefers a false redaction
24
+ * to a leak).
25
+ */
26
+ const SENSITIVE_KEY_FRAGMENTS = [
27
+ 'seed',
28
+ 'mnemonic',
29
+ 'privatekey',
30
+ 'secret',
31
+ 'password',
32
+ 'passphrase',
33
+ 'signature',
34
+ 'message',
35
+ 'payload',
36
+ 'token',
37
+ 'apikey',
38
+ 'authorization',
39
+ 'auth'
40
+ ];
41
+ const MAX_STRING_LENGTH = 500;
42
+ const MAX_DEPTH = 4;
43
+ const REDACTED = '[Redacted]';
44
+ function isSensitiveKey(key) {
45
+ // Strip separators so `private_key` / `x-api-key` normalize to the bare form.
46
+ const normalized = key.toLowerCase().split('-').join('').split('_').join('');
47
+ return SENSITIVE_KEY_FRAGMENTS.some(fragment => normalized.includes(fragment));
48
+ }
49
+ // Content patterns for secrets embedded in FREE-FORM strings (error messages,
50
+ // log args) where key-based redaction can't reach. All are linear, anchored,
51
+ // single-quantifier expressions — no nested/overlapping quantifiers — so they
52
+ // carry no catastrophic-backtracking (ReDoS) risk on attacker-controlled input.
53
+ const SECRET_CONTENT_PATTERNS = [
54
+ // 0x-prefixed hex blobs: EVM addresses (40 nibbles) and longer
55
+ // signatures/keys/hashes/calldata (64+). Over-redacts public hashes by design.
56
+ [/0x[a-fA-F0-9]{40,}/g, '0x[redacted-hex]'],
57
+ // `secret=…` / `token=…` / `key=…` query-string or k/v fragments.
58
+ [
59
+ /\b(token|key|secret|auth|sig|signature|password|passphrase|mnemonic|seed|apikey)=[^\s&"']+/gi,
60
+ '$1=[redacted]'
61
+ ],
62
+ // `Bearer <opaque>` / `Basic <opaque>` authorization values.
63
+ [/\b(bearer|basic)\s+[A-Za-z0-9._~+/-]+=*/gi, '$1 [redacted]']
64
+ ];
65
+ /**
66
+ * Redact secrets that live INSIDE a free-form string. Best-effort and
67
+ * deliberately conservative toward over-redaction: a wallet error string can
68
+ * embed an address, a reverted-tx calldata blob, or a URL with a token, none of
69
+ * which key-based redaction sees. Two documented best-effort gaps, both left
70
+ * unmatched to avoid destroying legitimate data:
71
+ * - Base58 (Solana) / TON `EQ…` addresses — indistinguishable from ordinary
72
+ * words without false positives.
73
+ * - BARE (non-`0x`) hex blobs — a raw 64-char hex string is far more often a
74
+ * legitimate id/hash than a secret, so only `0x`-prefixed hex is redacted.
75
+ * Structured telemetry fields never carry addresses regardless (see telemetry.ts);
76
+ * this pass only hardens the free-form `error.message` / log-arg surface.
77
+ */
78
+ function redactSecretsInString(input) {
79
+ let out = input;
80
+ for (const [pattern, replacement] of SECRET_CONTENT_PATTERNS) {
81
+ out = out.replace(pattern, replacement);
82
+ }
83
+ return out;
84
+ }
85
+ /**
86
+ * Recursively copy `value`, redacting sensitive object-keys, capping long
87
+ * strings, and rendering non-serializable values inert. The returned objects use
88
+ * a null prototype so a malicious `__proto__`/`constructor` key in attacker-shaped
89
+ * input can't pollute `Object.prototype` downstream.
90
+ */
91
+ export function scrubValue(value, depth = 0) {
92
+ if (depth > MAX_DEPTH)
93
+ return '[Truncated: max depth]';
94
+ if (value === null || value === undefined)
95
+ return value;
96
+ const type = typeof value;
97
+ if (type === 'string') {
98
+ // Redact embedded secrets BEFORE capping, so the cap can't slice a pattern
99
+ // in half and leak its tail.
100
+ const str = redactSecretsInString(value);
101
+ return str.length > MAX_STRING_LENGTH
102
+ ? str.slice(0, MAX_STRING_LENGTH) + '…[truncated]'
103
+ : str;
104
+ }
105
+ if (type === 'number' || type === 'boolean')
106
+ return value;
107
+ if (type === 'bigint')
108
+ return value.toString();
109
+ if (type === 'function' || type === 'symbol')
110
+ return `[${type}]`;
111
+ if (Array.isArray(value)) {
112
+ return value.map(item => scrubValue(item, depth + 1));
113
+ }
114
+ if (type === 'object') {
115
+ // Error instances carry a useful message but also a stack we don't want.
116
+ if (value instanceof Error) {
117
+ return { name: value.name, message: scrubValue(value.message, depth + 1) };
118
+ }
119
+ const out = Object.create(null);
120
+ for (const [key, val] of Object.entries(value)) {
121
+ out[key] = isSensitiveKey(key) ? REDACTED : scrubValue(val, depth + 1);
122
+ }
123
+ return out;
124
+ }
125
+ return '[Unserializable]';
126
+ }
127
+ /** Scrub a free-form error message (string in → capped string out). */
128
+ export function scrubMessage(message) {
129
+ return scrubValue(message);
130
+ }
131
+ /** Scrub a variadic `args` array before it reaches `observer.onLog`. */
132
+ export function scrubArgs(args) {
133
+ return args.map(arg => scrubValue(arg));
134
+ }
135
+ //# sourceMappingURL=scrub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrub.js","sourceRoot":"","sources":["../../src/observability/scrub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import type { ConnectionMode, WalletError, WalletFlowType, Platform, LogLevel, Namespace, NetworkId } from '@meshconnect/uwc-types';
2
+ import type { UWCEventName, UWCEventMap, UWCOperation } from '../events';
3
+ /**
4
+ * A normalized, PII-safe telemetry record derived from a `UWCEventMap` event.
5
+ *
6
+ * This is the ONLY shape that reaches `observer.onEvent`. It is the PII boundary:
7
+ * it has no field for a signing payload, signature, raw transaction, or wallet
8
+ * address — leaking those is structurally impossible, not merely discouraged.
9
+ */
10
+ export interface UWCTelemetryEvent {
11
+ /** The originating event name. */
12
+ name: UWCEventName;
13
+ /** Per-connector-instance correlation id (stamped on every event). */
14
+ sdkSessionId: string;
15
+ /** ms since epoch when the record was produced. */
16
+ timestamp: number;
17
+ /** Wallet-boundary op, when the event relates to one. */
18
+ operation?: UWCOperation | undefined;
19
+ connectionMode?: ConnectionMode | undefined;
20
+ /** CAIP namespace (eip155 | solana | tron | tvm …) — never an address. */
21
+ namespace?: Namespace | undefined;
22
+ /** CAIP chain id (e.g. `eip155:1`) — never an address. */
23
+ chainId?: NetworkId | undefined;
24
+ /** Catalog wallet id — never an address. */
25
+ walletId?: string | undefined;
26
+ /** Correlates a handoff start with its terminal. */
27
+ handoffId?: string | undefined;
28
+ durationMs?: number | undefined;
29
+ walletFlowType?: WalletFlowType | undefined;
30
+ platform?: Platform | undefined;
31
+ /** Present on failure events. `message` is scrubbed before egress. */
32
+ error?: WalletError | undefined;
33
+ /** Log level, present only for `name === 'log'`. */
34
+ level?: LogLevel | undefined;
35
+ }
36
+ /**
37
+ * Vendor-agnostic telemetry sink. Consumers implement; UWC ships no vendor SDK.
38
+ * Both methods are optional and called best-effort (throws are swallowed upstream).
39
+ *
40
+ * CONTRACT — both methods are invoked **synchronously on the wallet-operation
41
+ * path**. A terminal event (e.g. `walletSucceeded`) fires between the wallet
42
+ * returning a result and that result reaching the caller, so a slow handler adds
43
+ * latency to connect / sign / sendTransaction. Keep handlers **non-blocking**:
44
+ * hand off to a queue-backed sink (Datadog `addAction`, Segment `track`, etc. are
45
+ * already non-blocking) or defer heavy work yourself — e.g.
46
+ * `onEvent: e => queueMicrotask(() => doHeavyWork(e))`. Do not perform synchronous
47
+ * network/IO or expensive serialization inline.
48
+ */
49
+ export interface UWCObserver {
50
+ /** A normalized, PII-safe event. Forward to Datadog / Amplitude / Segment / … (non-blocking). */
51
+ onEvent?(event: UWCTelemetryEvent): void;
52
+ /** A log line (all levels). `args` is already scrubbed. Keep non-blocking. */
53
+ onLog?(level: LogLevel, message: string, args: unknown[]): void;
54
+ }
55
+ /**
56
+ * Best-effort coarse runtime surface. HEURISTIC — user agents lie and in-app
57
+ * browsers are hard to detect; treat `webview` as a hint, not a guarantee.
58
+ * Never returns the raw UA (PII / high-cardinality).
59
+ */
60
+ export declare function derivePlatform(): Platform;
61
+ /**
62
+ * Derive the flow taxonomy from connection mode + surface. HEURISTIC — UWC does
63
+ * not always know whether a WalletConnect handoff used a deeplink vs a QR scan;
64
+ * we infer from the platform (mobile ⇒ deeplink, desktop ⇒ QR), matching link-v2's
65
+ * historical intent. Consumers with better context may override downstream.
66
+ */
67
+ export declare function deriveWalletFlowType(connectionMode: ConnectionMode, platform: Platform): WalletFlowType;
68
+ /**
69
+ * Map a raw `UWCEventMap` event to its PII-safe `UWCTelemetryEvent`.
70
+ *
71
+ * Per-event allow-listing: only known-safe fields are copied. Payloads carrying a
72
+ * `Session`/`Network` (which include the wallet address) are reduced to
73
+ * `namespace` + `chainId` + `walletId`. Unknown/noisy payloads collapse to just
74
+ * the name. `error.message` is scrubbed.
75
+ */
76
+ export declare function normalizeEvent<K extends UWCEventName>(name: K, data: UWCEventMap[K], sdkSessionId: string, now: number): UWCTelemetryEvent;
77
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/observability/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACV,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAGxE;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,IAAI,EAAE,YAAY,CAAA;IAClB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAA;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IACpC,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IAC3C,0EAA0E;IAC1E,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACjC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,cAAc,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IAC3C,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC/B,sEAAsE;IACtE,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC/B,oDAAoD;IACpD,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,iGAAiG;IACjG,OAAO,CAAC,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAA;IACxC,8EAA8E;IAC9E,KAAK,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAChE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAWzC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAehB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,YAAY,EACnD,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,YAAY,EAAE,MAAM,EACpB,GAAG,EAAE,MAAM,GACV,iBAAiB,CAuFnB"}