@meshconnect/uwc-types 0.16.1 → 0.17.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.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Bounded first-seen dedup — the shared "storm guard" for error-reporting
3
+ * sites (observer sinks, relay/bridge error callbacks). Evicts wholesale at
4
+ * the cap: it is a storm guard, not a permanent ledger, so a previously-seen
5
+ * key may report once more after eviction — an accepted trade for a bounded
6
+ * footprint. (`uwc-bridge-child` keeps a local copy of this logic to stay
7
+ * dependency-free; keep the two in sync.)
8
+ */
9
+ export declare function createBoundedDedup(maxEntries: number): {
10
+ /** true the FIRST time a key is seen (and records it); false on repeats. */
11
+ firstSeen(key: string): boolean;
12
+ };
13
+ //# sourceMappingURL=bounded-dedup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-dedup.d.ts","sourceRoot":"","sources":["../src/bounded-dedup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG;IACtD,4EAA4E;IAC5E,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAChC,CAUA"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bounded first-seen dedup — the shared "storm guard" for error-reporting
3
+ * sites (observer sinks, relay/bridge error callbacks). Evicts wholesale at
4
+ * the cap: it is a storm guard, not a permanent ledger, so a previously-seen
5
+ * key may report once more after eviction — an accepted trade for a bounded
6
+ * footprint. (`uwc-bridge-child` keeps a local copy of this logic to stay
7
+ * dependency-free; keep the two in sync.)
8
+ */
9
+ export function createBoundedDedup(maxEntries) {
10
+ const seen = new Set();
11
+ return {
12
+ firstSeen(key) {
13
+ if (seen.has(key))
14
+ return false;
15
+ if (seen.size >= maxEntries)
16
+ seen.clear();
17
+ seen.add(key);
18
+ return true;
19
+ }
20
+ };
21
+ }
22
+ //# sourceMappingURL=bounded-dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-dedup.js","sourceRoot":"","sources":["../src/bounded-dedup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IAInD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,OAAO;QACL,SAAS,CAAC,GAAW;YACnB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAA;YAC/B,IAAI,IAAI,CAAC,IAAI,IAAI,UAAU;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAA;YACzC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACb,OAAO,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1,8 +1,41 @@
1
1
  import type { Network, NetworkId, Namespace } from './networks';
2
+ import type { ConnectionMode } from './connection-mode';
2
3
  import type { AvailableAddress } from './session';
3
4
  import type { DetectedEIP6963WalletInfo, DetectedSolanaWalletInfo, DetectedTronWalletInfo, DetectedTonWalletInfo, ExtensionInjectedProvider, IntegratedBrowserInjectedProvider, WalletConnectProvider, TonConnectWalletProvider, WalletMetadata } from './UWC-state';
4
5
  import type { EVMCapabilities, TransactionRequest, TransactionResult } from './transactions';
5
6
  import type { SignatureType, EIP712TypedData } from './signature';
7
+ /**
8
+ * A post-connect wallet lifecycle change observed from the live provider.
9
+ *
10
+ * PII-safe by construction: there is NO address field. For `accountChanged` we
11
+ * signal only THAT the account/list changed (the new address is dropped at the
12
+ * connector boundary). `chainChanged` carries the new CAIP-2 id (non-PII) when
13
+ * resolvable.
14
+ */
15
+ export type WalletLifecycleEvent = {
16
+ type: 'accountChanged';
17
+ } | {
18
+ type: 'chainChanged';
19
+ chainId?: NetworkId;
20
+ } | {
21
+ type: 'disconnect';
22
+ } | {
23
+ type: 'sessionExpired';
24
+ };
25
+ /**
26
+ * Names the session a lifecycle subscription is being attached FOR. The caller
27
+ * (core) resolves it from the active session and passes it explicitly — a
28
+ * connector must never infer it from its own ambient state, which can be
29
+ * ambiguous (a secondary-namespace gather leaves more than one internal
30
+ * service holding a live provider surface).
31
+ */
32
+ export interface WalletLifecycleContext {
33
+ /** Transport of the session being observed. */
34
+ connectionMode: ConnectionMode;
35
+ /** ACTIVE namespace of the session — the surface the connector must observe. */
36
+ namespace?: Namespace;
37
+ walletId?: string;
38
+ }
6
39
  /**
7
40
  * Result interface for connect operations
8
41
  */
@@ -89,5 +122,20 @@ export interface Connector {
89
122
  * signature, the relay broadcasts. Optional; not all connectors support it.
90
123
  */
91
124
  signSolanaTransactionBytes?(serializedTx: Uint8Array): Promise<Uint8Array>;
125
+ /**
126
+ * Subscribe to post-connect wallet lifecycle changes from the live provider
127
+ * (account/chain switched in the wallet, disconnect, session expiry). For
128
+ * OBSERVABILITY only — it never changes connection behaviour.
129
+ *
130
+ * Implementations MUST NOT include an address in the emitted event (the PII
131
+ * boundary stays in core). The caller passes a `WalletLifecycleContext`
132
+ * naming the session being observed (active namespace / transport / wallet);
133
+ * implementations route by it rather than inferring from internal state.
134
+ * Returns an unsubscribe function, or `undefined` when this connector/session
135
+ * can't be observed (no live `.on`, unsupported namespace) — the core then
136
+ * emits `lifecycleTelemetryUnavailable`. Optional: a connector that never
137
+ * observes lifecycle leaves it undefined entirely.
138
+ */
139
+ onLifecycleEvent?(listener: (event: WalletLifecycleEvent) => void, context: WalletLifecycleContext): (() => void) | undefined;
92
140
  }
93
141
  //# sourceMappingURL=connector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,KAAK,EACV,yBAAyB,EACzB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,EACzB,iCAAiC,EACjC,qBAAqB,EACrB,wBAAwB,EACxB,cAAc,EACf,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEjE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,SAAS,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,EAAE,gBAAgB,EAAE,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,SAAS,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAA;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IAEH,OAAO,CACL,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EACL,yBAAyB,GACzB,iCAAiC,GACjC,qBAAqB,GACrB,wBAAwB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAAA;IAE3B;;;;;;;OAOG;IACH,gBAAgB,CAAC,IAAI,MAAM,CAAA;IAE3B;;;;OAIG;IACH,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5B;;;;;OAKG;IAEH,aAAa,CACX,OAAO,EAAE,OAAO,EAChB,QAAQ,CAAC,EACL,yBAAyB,GACzB,iCAAiC,GACjC,qBAAqB,GACrB,wBAAwB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAE/B;;;;;;OAMG;IACH,mBAAmB,CAAC,CAClB,SAAS,EAAE,SAAS,EACpB,eAAe,CAAC,EAAE,cAAc,EAAE,GACjC,OAAO,CACN,yBAAyB,EAAE,GAC3B,wBAAwB,EAAE,GAC1B,sBAAsB,EAAE,GACxB,qBAAqB,EAAE,CAC1B,CAAA;IAED;;;;;OAKG;IACH,WAAW,CAAC,CACV,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EACL,yBAAyB,GACzB,iCAAiC,GACjC,qBAAqB,GACrB,wBAAwB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAA;IAEzB;;;;;OAKG;IACH,aAAa,CAAC,CACZ,SAAS,EAAE,eAAe,EAC1B,QAAQ,CAAC,EACL,yBAAyB,GACzB,iCAAiC,GACjC,qBAAqB,GACxB,OAAO,CAAC,MAAM,CAAC,CAAA;IAElB;;;;;OAKG;IACH,eAAe,CAAC,CACd,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,CAAC,EACL,yBAAyB,GACzB,iCAAiC,GACjC,qBAAqB,GACrB,wBAAwB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAE7B,qBAAqB,CAAC,CACpB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EAAE,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAA;IAE3C;;;;OAIG;IACH,0BAA0B,CAAC,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAC3E"}
1
+ {"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -11,4 +11,19 @@ export declare class WalletConnectorError extends Error {
11
11
  type: ErrorType;
12
12
  constructor(walletError: WalletError);
13
13
  }
14
+ /**
15
+ * Thrown at connect time when the wallet's supported networks and the configured
16
+ * networks share no common chain, so there is nothing to connect on. Carries both
17
+ * CAIP-2 id lists (non-PII) so telemetry can answer "wallet offered X, token
18
+ * configured Y" — the difference between a metadata gap, a client-config mistake,
19
+ * and a genuinely-unsupported wallet. It is the single most frequent connection
20
+ * failure in production, so it gets a dedicated, self-describing error.
21
+ */
22
+ export declare class NoCommonNetworkError extends WalletConnectorError {
23
+ /** CAIP-2 ids the wallet's provider advertised. */
24
+ readonly walletNetworkIds: string[];
25
+ /** CAIP-2 ids configured on this UWC instance. */
26
+ readonly configuredNetworkIds: string[];
27
+ constructor(walletNetworkIds: string[], configuredNetworkIds: string[]);
28
+ }
14
29
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;AAE1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,EAAE,SAAS,CAAA;gBAEH,WAAW,EAAE,WAAW;CAKrC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;AAE1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,EAAE,SAAS,CAAA;gBAEH,WAAW,EAAE,WAAW;CAKrC;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAqB,SAAQ,oBAAoB;IAC5D,mDAAmD;IACnD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAA;IACnC,kDAAkD;IAClD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,EAAE,CAAA;gBAE3B,gBAAgB,EAAE,MAAM,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE;CAUvE"}
package/dist/errors.js CHANGED
@@ -10,4 +10,27 @@ export class WalletConnectorError extends Error {
10
10
  this.type = walletError.type;
11
11
  }
12
12
  }
13
+ /**
14
+ * Thrown at connect time when the wallet's supported networks and the configured
15
+ * networks share no common chain, so there is nothing to connect on. Carries both
16
+ * CAIP-2 id lists (non-PII) so telemetry can answer "wallet offered X, token
17
+ * configured Y" — the difference between a metadata gap, a client-config mistake,
18
+ * and a genuinely-unsupported wallet. It is the single most frequent connection
19
+ * failure in production, so it gets a dedicated, self-describing error.
20
+ */
21
+ export class NoCommonNetworkError extends WalletConnectorError {
22
+ /** CAIP-2 ids the wallet's provider advertised. */
23
+ walletNetworkIds;
24
+ /** CAIP-2 ids configured on this UWC instance. */
25
+ configuredNetworkIds;
26
+ constructor(walletNetworkIds, configuredNetworkIds) {
27
+ super({
28
+ type: 'unknown',
29
+ message: 'No common supported network found between wallet and configured networks'
30
+ });
31
+ this.name = 'NoCommonNetworkError';
32
+ this.walletNetworkIds = walletNetworkIds;
33
+ this.configuredNetworkIds = configuredNetworkIds;
34
+ }
35
+ }
13
36
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,CAAW;IAEf,YAAY,WAAwB;QAClC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAC1B,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAA;IAC9B,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,CAAW;IAEf,YAAY,WAAwB;QAClC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAC1B,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;QAClC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAA;IAC9B,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAqB,SAAQ,oBAAoB;IAC5D,mDAAmD;IAC1C,gBAAgB,CAAU;IACnC,kDAAkD;IACzC,oBAAoB,CAAU;IAEvC,YAAY,gBAA0B,EAAE,oBAA8B;QACpE,KAAK,CAAC;YACJ,IAAI,EAAE,SAAS;YACf,OAAO,EACL,0EAA0E;SAC7E,CAAC,CAAA;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;QAClC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;QACxC,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAA;IAClD,CAAC;CACF"}
package/dist/index.d.ts CHANGED
@@ -12,4 +12,7 @@ export * from './signature';
12
12
  export * from './transactions';
13
13
  export * from './errors';
14
14
  export * from './solana-message';
15
+ export * from './observability';
16
+ export * from './bounded-dedup';
17
+ export * from './safe-error-message';
15
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA"}
package/dist/index.js CHANGED
@@ -12,4 +12,7 @@ export * from './signature';
12
12
  export * from './transactions';
13
13
  export * from './errors';
14
14
  export * from './solana-message';
15
+ export * from './observability';
16
+ export * from './bounded-dedup';
17
+ export * from './safe-error-message';
15
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Vendor-agnostic observability primitives for the Universal Wallet Connector.
3
+ *
4
+ * These are leaf types — no dependency on the core event bus — so any package
5
+ * (including consumers below UWC in the dep graph, e.g. link-v2) can import them
6
+ * without pulling in `@meshconnect/uwc-core`. The richer `UWCTelemetryEvent` /
7
+ * `UWCObserver` shapes live in `@meshconnect/uwc-core` because they reference the
8
+ * core event names.
9
+ *
10
+ * UWC ships NO third-party telemetry SDK. The consumer implements the sinks and
11
+ * wires them to whatever backend they run (Datadog, Amplitude, Segment, …).
12
+ */
13
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
14
+ /**
15
+ * Pluggable logger. The default implementation (`createLogger`) is console-backed
16
+ * and level-gated; inject your own to redirect UWC's internal logging into your
17
+ * stack. Kept to the four standard levels so a consumer's existing logger usually
18
+ * satisfies it without an adapter.
19
+ */
20
+ export interface Logger {
21
+ debug(message: string, ...args: unknown[]): void;
22
+ info(message: string, ...args: unknown[]): void;
23
+ warn(message: string, ...args: unknown[]): void;
24
+ error(message: string, ...args: unknown[]): void;
25
+ }
26
+ /**
27
+ * Canonical wallet-handoff flow taxonomy.
28
+ *
29
+ * UWC is the single owner of this union so the two consumer layers (link-v2 and
30
+ * v3) don't drift. The string values are deliberately frozen to match link-v2's
31
+ * historical `wallet_flow_type` wire values so existing RUM / Segment facets keep
32
+ * matching after a consumer switches to importing this type — this is a
33
+ * lift-and-canonicalize, not a rename.
34
+ *
35
+ * Note: UWC's own `deriveWalletFlowType` only produces the extension / WC / TON /
36
+ * in-wallet / unknown values. The `dapp_*` members are part of the frozen wire
37
+ * taxonomy but are set only by a consumer with richer context — forward-compat,
38
+ * not dead code.
39
+ */
40
+ export type WalletFlowType = 'browser_extension_flow' | 'wallet_connect_deep_link_flow' | 'wallet_connect_qr_flow' | 'dapp_deep_link_flow' | 'dapp_qr_flow' | 'ton_connect_qr_flow' | 'ton_connect_deep_link_flow' | 'in_wallet_browser_flow' | 'unknown';
41
+ /**
42
+ * Coarse runtime surface. Intentionally a small enum — never a raw user-agent
43
+ * string, which is PII and high-cardinality. Used to decide whether mobile-only
44
+ * heuristics (e.g. focus bracketing) apply to a handoff.
45
+ */
46
+ export type Platform = 'mobile' | 'desktop' | 'webview' | 'unknown';
47
+ /**
48
+ * Coarse cause bucket for a non-success wallet outcome. Deliberately small and
49
+ * stable so it can drive failure dashboards without high cardinality. `unknown`
50
+ * is the honest default — UWC only classifies a kind when the evidence is
51
+ * reliable (a known EIP-1193 code, an explicit rejection, a typed connector
52
+ * error), never a guess from a message substring.
53
+ */
54
+ export type FailureKind = 'user_rejected' | 'provider_error' | 'bridge_error' | 'network_error'
55
+ /** Wallet and the configured networks share no chain — connect can't proceed. */
56
+ | 'no_common_network' | 'unsupported_method' | 'invalid_params' | 'expired' | 'unknown';
57
+ /**
58
+ * Where a failure originated, as far as UWC can tell from the error shape. Used
59
+ * to triage whether a problem sits in the wallet/provider, our connector layer,
60
+ * or the iframe bridge. `unknown` when the shape carries no reliable signal.
61
+ */
62
+ export type ErrorSource = 'provider' | 'connector' | 'bridge' | 'uwc' | 'unknown';
63
+ /**
64
+ * Why post-connect wallet-lifecycle telemetry could not be attached. Emitted ONCE
65
+ * per connector/session/namespace so analytics can distinguish "lifecycle is
66
+ * genuinely silent" from "we never wired it" — an absent signal is itself signal.
67
+ */
68
+ export type LifecycleUnavailableReason =
69
+ /** The live provider exposes no `.on` (or the connected namespace has no event surface yet). */
70
+ 'provider_subscription_unavailable'
71
+ /** The iframe bridge couldn't forward provider events (no forwarder on the parent). */
72
+ | 'bridge_subscription_unavailable'
73
+ /** The connected namespace has no lifecycle source UWC observes. */
74
+ | 'unsupported_namespace'
75
+ /**
76
+ * RESERVED — not yet emitted. The parent's `uwc-bridge-parent` predates event
77
+ * forwarding; produced once cross-bridge lifecycle forwarding ships (MFS-576).
78
+ * Declared now so consumer switch statements are exhaustive ahead of it.
79
+ */
80
+ | 'old_bridge_parent'
81
+ /**
82
+ * Lifecycle telemetry stopped mid-session: the per-attachment emission cap
83
+ * tripped (a storming provider). Emitted once at the moment of the trip.
84
+ */
85
+ | 'emission_cap';
86
+ //# sourceMappingURL=observability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CACjD;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GACtB,wBAAwB,GACxB,+BAA+B,GAC/B,wBAAwB,GACxB,qBAAqB,GACrB,cAAc,GACd,qBAAqB,GACrB,4BAA4B,GAC5B,wBAAwB,GACxB,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;AAEnE;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,eAAe;AACjB,iFAAiF;GAC/E,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,GAChB,SAAS,GACT,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,WAAW,GACX,QAAQ,GACR,KAAK,GACL,SAAS,CAAA;AAEb;;;;GAIG;AACH,MAAM,MAAM,0BAA0B;AACpC,gGAAgG;AAC9F,mCAAmC;AACrC,uFAAuF;GACrF,iCAAiC;AACnC,oEAAoE;GAClE,uBAAuB;AACzB;;;;GAIG;GACD,mBAAmB;AACrB;;;GAGG;GACD,cAAc,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Vendor-agnostic observability primitives for the Universal Wallet Connector.
3
+ *
4
+ * These are leaf types — no dependency on the core event bus — so any package
5
+ * (including consumers below UWC in the dep graph, e.g. link-v2) can import them
6
+ * without pulling in `@meshconnect/uwc-core`. The richer `UWCTelemetryEvent` /
7
+ * `UWCObserver` shapes live in `@meshconnect/uwc-core` because they reference the
8
+ * core event names.
9
+ *
10
+ * UWC ships NO third-party telemetry SDK. The consumer implements the sinks and
11
+ * wires them to whatever backend they run (Datadog, Amplitude, Segment, …).
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Extract a bounded message from any thrown value without ever throwing.
3
+ * Error-reporting sites run inside catch blocks and third-party emitters,
4
+ * where a poisoned `message` getter or a throwing `toString()` would replace
5
+ * the real failure with its own — and an unbounded message retained as a
6
+ * dedup key is a memory footgun. (`uwc-bridge-child` keeps a local copy —
7
+ * that package stays dependency-free; keep the two in sync.)
8
+ */
9
+ export declare function safeErrorMessage(error: unknown): string;
10
+ //# sourceMappingURL=safe-error-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-error-message.d.ts","sourceRoot":"","sources":["../src/safe-error-message.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASvD"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Length cap for extracted messages. Aligned with the core scrubber's
3
+ * MAX_STRING_LENGTH so an error-reporting site never retains (dedup keys) or
4
+ * forwards more than the telemetry layer would keep anyway.
5
+ */
6
+ const MAX_SAFE_ERROR_MESSAGE_LENGTH = 500;
7
+ /**
8
+ * Extract a bounded message from any thrown value without ever throwing.
9
+ * Error-reporting sites run inside catch blocks and third-party emitters,
10
+ * where a poisoned `message` getter or a throwing `toString()` would replace
11
+ * the real failure with its own — and an unbounded message retained as a
12
+ * dedup key is a memory footgun. (`uwc-bridge-child` keeps a local copy —
13
+ * that package stays dependency-free; keep the two in sync.)
14
+ */
15
+ export function safeErrorMessage(error) {
16
+ try {
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ return message.length > MAX_SAFE_ERROR_MESSAGE_LENGTH
19
+ ? message.slice(0, MAX_SAFE_ERROR_MESSAGE_LENGTH)
20
+ : message;
21
+ }
22
+ catch {
23
+ return 'Unreadable error object';
24
+ }
25
+ }
26
+ //# sourceMappingURL=safe-error-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-error-message.js","sourceRoot":"","sources":["../src/safe-error-message.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,6BAA6B,GAAG,GAAG,CAAA;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,OAAO,OAAO,CAAC,MAAM,GAAG,6BAA6B;YACnD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,6BAA6B,CAAC;YACjD,CAAC,CAAC,OAAO,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,yBAAyB,CAAA;IAClC,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshconnect/uwc-types",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "description": "TypeScript types for Universal Wallet Connector",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { createBoundedDedup } from './bounded-dedup'
3
+
4
+ describe('createBoundedDedup', () => {
5
+ it('reports a key only the first time it is seen', () => {
6
+ const dedup = createBoundedDedup(10)
7
+ expect(dedup.firstSeen('a')).toBe(true)
8
+ expect(dedup.firstSeen('a')).toBe(false)
9
+ expect(dedup.firstSeen('b')).toBe(true)
10
+ })
11
+
12
+ it('evicts wholesale at the cap, so a bounded footprint beats permanence', () => {
13
+ const dedup = createBoundedDedup(2)
14
+ expect(dedup.firstSeen('a')).toBe(true)
15
+ expect(dedup.firstSeen('b')).toBe(true)
16
+ // Cap hit — the set clears, then records 'c'.
17
+ expect(dedup.firstSeen('c')).toBe(true)
18
+ // 'a' was evicted: it may report once more (storm guard, not a ledger).
19
+ expect(dedup.firstSeen('a')).toBe(true)
20
+ // 'c' survived the post-eviction add.
21
+ expect(dedup.firstSeen('c')).toBe(false)
22
+ })
23
+ })
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bounded first-seen dedup — the shared "storm guard" for error-reporting
3
+ * sites (observer sinks, relay/bridge error callbacks). Evicts wholesale at
4
+ * the cap: it is a storm guard, not a permanent ledger, so a previously-seen
5
+ * key may report once more after eviction — an accepted trade for a bounded
6
+ * footprint. (`uwc-bridge-child` keeps a local copy of this logic to stay
7
+ * dependency-free; keep the two in sync.)
8
+ */
9
+ export function createBoundedDedup(maxEntries: number): {
10
+ /** true the FIRST time a key is seen (and records it); false on repeats. */
11
+ firstSeen(key: string): boolean
12
+ } {
13
+ const seen = new Set<string>()
14
+ return {
15
+ firstSeen(key: string): boolean {
16
+ if (seen.has(key)) return false
17
+ if (seen.size >= maxEntries) seen.clear()
18
+ seen.add(key)
19
+ return true
20
+ }
21
+ }
22
+ }
package/src/connector.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Network, NetworkId, Namespace } from './networks'
2
+ import type { ConnectionMode } from './connection-mode'
2
3
  import type { AvailableAddress } from './session'
3
4
  import type {
4
5
  DetectedEIP6963WalletInfo,
@@ -18,6 +19,35 @@ import type {
18
19
  } from './transactions'
19
20
  import type { SignatureType, EIP712TypedData } from './signature'
20
21
 
22
+ /**
23
+ * A post-connect wallet lifecycle change observed from the live provider.
24
+ *
25
+ * PII-safe by construction: there is NO address field. For `accountChanged` we
26
+ * signal only THAT the account/list changed (the new address is dropped at the
27
+ * connector boundary). `chainChanged` carries the new CAIP-2 id (non-PII) when
28
+ * resolvable.
29
+ */
30
+ export type WalletLifecycleEvent =
31
+ | { type: 'accountChanged' }
32
+ | { type: 'chainChanged'; chainId?: NetworkId }
33
+ | { type: 'disconnect' }
34
+ | { type: 'sessionExpired' }
35
+
36
+ /**
37
+ * Names the session a lifecycle subscription is being attached FOR. The caller
38
+ * (core) resolves it from the active session and passes it explicitly — a
39
+ * connector must never infer it from its own ambient state, which can be
40
+ * ambiguous (a secondary-namespace gather leaves more than one internal
41
+ * service holding a live provider surface).
42
+ */
43
+ export interface WalletLifecycleContext {
44
+ /** Transport of the session being observed. */
45
+ connectionMode: ConnectionMode
46
+ /** ACTIVE namespace of the session — the surface the connector must observe. */
47
+ namespace?: Namespace
48
+ walletId?: string
49
+ }
50
+
21
51
  /**
22
52
  * Result interface for connect operations
23
53
  */
@@ -162,4 +192,23 @@ export interface Connector {
162
192
  * signature, the relay broadcasts. Optional; not all connectors support it.
163
193
  */
164
194
  signSolanaTransactionBytes?(serializedTx: Uint8Array): Promise<Uint8Array>
195
+
196
+ /**
197
+ * Subscribe to post-connect wallet lifecycle changes from the live provider
198
+ * (account/chain switched in the wallet, disconnect, session expiry). For
199
+ * OBSERVABILITY only — it never changes connection behaviour.
200
+ *
201
+ * Implementations MUST NOT include an address in the emitted event (the PII
202
+ * boundary stays in core). The caller passes a `WalletLifecycleContext`
203
+ * naming the session being observed (active namespace / transport / wallet);
204
+ * implementations route by it rather than inferring from internal state.
205
+ * Returns an unsubscribe function, or `undefined` when this connector/session
206
+ * can't be observed (no live `.on`, unsupported namespace) — the core then
207
+ * emits `lifecycleTelemetryUnavailable`. Optional: a connector that never
208
+ * observes lifecycle leaves it undefined entirely.
209
+ */
210
+ onLifecycleEvent?(
211
+ listener: (event: WalletLifecycleEvent) => void,
212
+ context: WalletLifecycleContext
213
+ ): (() => void) | undefined
165
214
  }
package/src/errors.ts CHANGED
@@ -18,3 +18,29 @@ export class WalletConnectorError extends Error {
18
18
  this.type = walletError.type
19
19
  }
20
20
  }
21
+
22
+ /**
23
+ * Thrown at connect time when the wallet's supported networks and the configured
24
+ * networks share no common chain, so there is nothing to connect on. Carries both
25
+ * CAIP-2 id lists (non-PII) so telemetry can answer "wallet offered X, token
26
+ * configured Y" — the difference between a metadata gap, a client-config mistake,
27
+ * and a genuinely-unsupported wallet. It is the single most frequent connection
28
+ * failure in production, so it gets a dedicated, self-describing error.
29
+ */
30
+ export class NoCommonNetworkError extends WalletConnectorError {
31
+ /** CAIP-2 ids the wallet's provider advertised. */
32
+ readonly walletNetworkIds: string[]
33
+ /** CAIP-2 ids configured on this UWC instance. */
34
+ readonly configuredNetworkIds: string[]
35
+
36
+ constructor(walletNetworkIds: string[], configuredNetworkIds: string[]) {
37
+ super({
38
+ type: 'unknown',
39
+ message:
40
+ 'No common supported network found between wallet and configured networks'
41
+ })
42
+ this.name = 'NoCommonNetworkError'
43
+ this.walletNetworkIds = walletNetworkIds
44
+ this.configuredNetworkIds = configuredNetworkIds
45
+ }
46
+ }
package/src/index.ts CHANGED
@@ -12,3 +12,6 @@ export * from './signature'
12
12
  export * from './transactions'
13
13
  export * from './errors'
14
14
  export * from './solana-message'
15
+ export * from './observability'
16
+ export * from './bounded-dedup'
17
+ export * from './safe-error-message'
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Vendor-agnostic observability primitives for the Universal Wallet Connector.
3
+ *
4
+ * These are leaf types — no dependency on the core event bus — so any package
5
+ * (including consumers below UWC in the dep graph, e.g. link-v2) can import them
6
+ * without pulling in `@meshconnect/uwc-core`. The richer `UWCTelemetryEvent` /
7
+ * `UWCObserver` shapes live in `@meshconnect/uwc-core` because they reference the
8
+ * core event names.
9
+ *
10
+ * UWC ships NO third-party telemetry SDK. The consumer implements the sinks and
11
+ * wires them to whatever backend they run (Datadog, Amplitude, Segment, …).
12
+ */
13
+
14
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error'
15
+
16
+ /**
17
+ * Pluggable logger. The default implementation (`createLogger`) is console-backed
18
+ * and level-gated; inject your own to redirect UWC's internal logging into your
19
+ * stack. Kept to the four standard levels so a consumer's existing logger usually
20
+ * satisfies it without an adapter.
21
+ */
22
+ export interface Logger {
23
+ debug(message: string, ...args: unknown[]): void
24
+ info(message: string, ...args: unknown[]): void
25
+ warn(message: string, ...args: unknown[]): void
26
+ error(message: string, ...args: unknown[]): void
27
+ }
28
+
29
+ /**
30
+ * Canonical wallet-handoff flow taxonomy.
31
+ *
32
+ * UWC is the single owner of this union so the two consumer layers (link-v2 and
33
+ * v3) don't drift. The string values are deliberately frozen to match link-v2's
34
+ * historical `wallet_flow_type` wire values so existing RUM / Segment facets keep
35
+ * matching after a consumer switches to importing this type — this is a
36
+ * lift-and-canonicalize, not a rename.
37
+ *
38
+ * Note: UWC's own `deriveWalletFlowType` only produces the extension / WC / TON /
39
+ * in-wallet / unknown values. The `dapp_*` members are part of the frozen wire
40
+ * taxonomy but are set only by a consumer with richer context — forward-compat,
41
+ * not dead code.
42
+ */
43
+ export type WalletFlowType =
44
+ | 'browser_extension_flow'
45
+ | 'wallet_connect_deep_link_flow'
46
+ | 'wallet_connect_qr_flow'
47
+ | 'dapp_deep_link_flow'
48
+ | 'dapp_qr_flow'
49
+ | 'ton_connect_qr_flow'
50
+ | 'ton_connect_deep_link_flow'
51
+ | 'in_wallet_browser_flow'
52
+ | 'unknown'
53
+
54
+ /**
55
+ * Coarse runtime surface. Intentionally a small enum — never a raw user-agent
56
+ * string, which is PII and high-cardinality. Used to decide whether mobile-only
57
+ * heuristics (e.g. focus bracketing) apply to a handoff.
58
+ */
59
+ export type Platform = 'mobile' | 'desktop' | 'webview' | 'unknown'
60
+
61
+ /**
62
+ * Coarse cause bucket for a non-success wallet outcome. Deliberately small and
63
+ * stable so it can drive failure dashboards without high cardinality. `unknown`
64
+ * is the honest default — UWC only classifies a kind when the evidence is
65
+ * reliable (a known EIP-1193 code, an explicit rejection, a typed connector
66
+ * error), never a guess from a message substring.
67
+ */
68
+ export type FailureKind =
69
+ | 'user_rejected'
70
+ | 'provider_error'
71
+ | 'bridge_error'
72
+ | 'network_error'
73
+ /** Wallet and the configured networks share no chain — connect can't proceed. */
74
+ | 'no_common_network'
75
+ | 'unsupported_method'
76
+ | 'invalid_params'
77
+ | 'expired'
78
+ | 'unknown'
79
+
80
+ /**
81
+ * Where a failure originated, as far as UWC can tell from the error shape. Used
82
+ * to triage whether a problem sits in the wallet/provider, our connector layer,
83
+ * or the iframe bridge. `unknown` when the shape carries no reliable signal.
84
+ */
85
+ export type ErrorSource =
86
+ | 'provider'
87
+ | 'connector'
88
+ | 'bridge'
89
+ | 'uwc'
90
+ | 'unknown'
91
+
92
+ /**
93
+ * Why post-connect wallet-lifecycle telemetry could not be attached. Emitted ONCE
94
+ * per connector/session/namespace so analytics can distinguish "lifecycle is
95
+ * genuinely silent" from "we never wired it" — an absent signal is itself signal.
96
+ */
97
+ export type LifecycleUnavailableReason =
98
+ /** The live provider exposes no `.on` (or the connected namespace has no event surface yet). */
99
+ | 'provider_subscription_unavailable'
100
+ /** The iframe bridge couldn't forward provider events (no forwarder on the parent). */
101
+ | 'bridge_subscription_unavailable'
102
+ /** The connected namespace has no lifecycle source UWC observes. */
103
+ | 'unsupported_namespace'
104
+ /**
105
+ * RESERVED — not yet emitted. The parent's `uwc-bridge-parent` predates event
106
+ * forwarding; produced once cross-bridge lifecycle forwarding ships (MFS-576).
107
+ * Declared now so consumer switch statements are exhaustive ahead of it.
108
+ */
109
+ | 'old_bridge_parent'
110
+ /**
111
+ * Lifecycle telemetry stopped mid-session: the per-attachment emission cap
112
+ * tripped (a storming provider). Emitted once at the moment of the trip.
113
+ */
114
+ | 'emission_cap'
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { safeErrorMessage } from './safe-error-message'
3
+
4
+ describe('safeErrorMessage', () => {
5
+ it('extracts the message from an Error', () => {
6
+ expect(safeErrorMessage(new Error('boom'))).toBe('boom')
7
+ })
8
+
9
+ it('stringifies non-Error values', () => {
10
+ expect(safeErrorMessage('plain string')).toBe('plain string')
11
+ expect(safeErrorMessage(42)).toBe('42')
12
+ })
13
+
14
+ it('never throws for a throwing toString()', () => {
15
+ const evil = {
16
+ toString(): string {
17
+ throw new Error('poisoned toString')
18
+ }
19
+ }
20
+ expect(safeErrorMessage(evil)).toBe('Unreadable error object')
21
+ })
22
+
23
+ it('never throws for a poisoned message getter', () => {
24
+ const evil = new Error('real')
25
+ Object.defineProperty(evil, 'message', {
26
+ get() {
27
+ throw new Error('poisoned message')
28
+ }
29
+ })
30
+ expect(safeErrorMessage(evil)).toBe('Unreadable error object')
31
+ })
32
+
33
+ it('caps a huge message so dedup keys stay bounded', () => {
34
+ const out = safeErrorMessage(new Error('x'.repeat(10_000)))
35
+ expect(out).toHaveLength(500)
36
+ })
37
+ })
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Length cap for extracted messages. Aligned with the core scrubber's
3
+ * MAX_STRING_LENGTH so an error-reporting site never retains (dedup keys) or
4
+ * forwards more than the telemetry layer would keep anyway.
5
+ */
6
+ const MAX_SAFE_ERROR_MESSAGE_LENGTH = 500
7
+
8
+ /**
9
+ * Extract a bounded message from any thrown value without ever throwing.
10
+ * Error-reporting sites run inside catch blocks and third-party emitters,
11
+ * where a poisoned `message` getter or a throwing `toString()` would replace
12
+ * the real failure with its own — and an unbounded message retained as a
13
+ * dedup key is a memory footgun. (`uwc-bridge-child` keeps a local copy —
14
+ * that package stays dependency-free; keep the two in sync.)
15
+ */
16
+ export function safeErrorMessage(error: unknown): string {
17
+ try {
18
+ const message = error instanceof Error ? error.message : String(error)
19
+ return message.length > MAX_SAFE_ERROR_MESSAGE_LENGTH
20
+ ? message.slice(0, MAX_SAFE_ERROR_MESSAGE_LENGTH)
21
+ : message
22
+ } catch {
23
+ return 'Unreadable error object'
24
+ }
25
+ }