@hub3js/std 0.0.1-next.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.
Files changed (72) hide show
  1. package/actions/package.json +8 -0
  2. package/builders/package.json +8 -0
  3. package/dist/actions/disconnect.d.ts +8 -0
  4. package/dist/actions/disconnect.d.ts.map +1 -0
  5. package/dist/actions/mod.d.ts +2 -0
  6. package/dist/actions/mod.d.ts.map +1 -0
  7. package/dist/actions/mod.js +2 -0
  8. package/dist/actions/mod.js.map +7 -0
  9. package/dist/builders/disconnect.d.ts +10 -0
  10. package/dist/builders/disconnect.d.ts.map +1 -0
  11. package/dist/builders/mod.d.ts +2 -0
  12. package/dist/builders/mod.d.ts.map +1 -0
  13. package/dist/builders/mod.js +2 -0
  14. package/dist/builders/mod.js.map +7 -0
  15. package/dist/hooks/changeAccountSubscriber.d.ts +89 -0
  16. package/dist/hooks/changeAccountSubscriber.d.ts.map +1 -0
  17. package/dist/hooks/mod.d.ts +2 -0
  18. package/dist/hooks/mod.d.ts.map +1 -0
  19. package/dist/hooks/mod.js +2 -0
  20. package/dist/hooks/mod.js.map +7 -0
  21. package/dist/operators/after.d.ts +4 -0
  22. package/dist/operators/after.d.ts.map +1 -0
  23. package/dist/operators/and.d.ts +6 -0
  24. package/dist/operators/and.d.ts.map +1 -0
  25. package/dist/operators/before.d.ts +8 -0
  26. package/dist/operators/before.d.ts.map +1 -0
  27. package/dist/operators/mod.d.ts +5 -0
  28. package/dist/operators/mod.d.ts.map +1 -0
  29. package/dist/operators/mod.js +2 -0
  30. package/dist/operators/mod.js.map +7 -0
  31. package/dist/operators/or.d.ts +15 -0
  32. package/dist/operators/or.d.ts.map +1 -0
  33. package/dist/types/accounts.d.ts +11 -0
  34. package/dist/types/accounts.d.ts.map +1 -0
  35. package/dist/types/mod.d.ts +3 -0
  36. package/dist/types/mod.d.ts.map +1 -0
  37. package/dist/types/mod.js +1 -0
  38. package/dist/types/mod.js.map +7 -0
  39. package/dist/types/namespace.d.ts +10 -0
  40. package/dist/types/namespace.d.ts.map +1 -0
  41. package/dist/utils/caip.d.ts +2 -0
  42. package/dist/utils/caip.d.ts.map +1 -0
  43. package/dist/utils/error.d.ts +11 -0
  44. package/dist/utils/error.d.ts.map +1 -0
  45. package/dist/utils/mod.d.ts +3 -0
  46. package/dist/utils/mod.d.ts.map +1 -0
  47. package/dist/utils/mod.js +2 -0
  48. package/dist/utils/mod.js.map +7 -0
  49. package/hooks/package.json +8 -0
  50. package/operators/package.json +8 -0
  51. package/package.json +61 -0
  52. package/readme.md +15 -0
  53. package/src/actions/disconnect.ts +16 -0
  54. package/src/actions/mod.ts +1 -0
  55. package/src/builders/disconnect.ts +19 -0
  56. package/src/builders/mod.ts +1 -0
  57. package/src/hooks/changeAccountSubscriber.test.ts +172 -0
  58. package/src/hooks/changeAccountSubscriber.ts +239 -0
  59. package/src/hooks/mod.ts +1 -0
  60. package/src/operators/after.ts +8 -0
  61. package/src/operators/and.ts +39 -0
  62. package/src/operators/before.ts +12 -0
  63. package/src/operators/mod.ts +11 -0
  64. package/src/operators/or.ts +19 -0
  65. package/src/types/accounts.ts +13 -0
  66. package/src/types/mod.ts +11 -0
  67. package/src/types/namespace.ts +26 -0
  68. package/src/utils/caip.ts +10 -0
  69. package/src/utils/error.ts +44 -0
  70. package/src/utils/mod.ts +2 -0
  71. package/types/package.json +8 -0
  72. package/utils/package.json +8 -0
@@ -0,0 +1,172 @@
1
+ import type { AutoImplementedActionsByRecommended } from '../types/namespace.js';
2
+ import type { Actions } from '@hub3js/core';
3
+
4
+ import { createStore, Namespace } from '@hub3js/core';
5
+ import { waitFor } from '@testing-library/dom';
6
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
7
+
8
+ import { ChangeAccountSubscriberBuilder } from './changeAccountSubscriber.js';
9
+
10
+ interface TestNamespaceActions {
11
+ disconnect: () => void;
12
+ }
13
+
14
+ describe('check changeAccountSubscriber', () => {
15
+ const garbageProvider = { name: 'garbage provider' };
16
+
17
+ const garbageFormatter = async (
18
+ _instance: typeof garbageProvider,
19
+ accounts: string[],
20
+ ) => accounts.map((account) => `Formatted: ${account}`);
21
+
22
+ const garbageAddEventListener = (
23
+ _instance: typeof garbageProvider,
24
+ callback: (event: string[]) => void,
25
+ ) => {
26
+ callback(['0']);
27
+ };
28
+
29
+ const garbageRemoveEventListener = (
30
+ _: typeof garbageProvider,
31
+ __: (event: string[]) => void,
32
+ ) => {};
33
+
34
+ let builder: ChangeAccountSubscriberBuilder<
35
+ string[],
36
+ typeof garbageProvider,
37
+ Actions<AutoImplementedActionsByRecommended>
38
+ >;
39
+
40
+ const setupNamespace = () => {
41
+ const actions = new Map();
42
+ const disconnectAction = vi.fn();
43
+ actions.set('disconnect', disconnectAction);
44
+
45
+ const store = createStore();
46
+ const ns = new Namespace<TestNamespaceActions>('evm', 'garbage provider', {
47
+ actions,
48
+ store,
49
+ });
50
+
51
+ const context = { action: ns.run.bind(ns), state: ns.state.bind(ns) };
52
+
53
+ return { ns, context, disconnectAction };
54
+ };
55
+
56
+ beforeEach(() => {
57
+ builder = new ChangeAccountSubscriberBuilder();
58
+ });
59
+
60
+ test('throws error if required operators are not set', () => {
61
+ expect(() => builder.build()).toThrow(
62
+ `Required "getInstance" operation has not been set for "changeAccountSubscriber"`,
63
+ );
64
+
65
+ builder.getInstance(() => garbageProvider);
66
+ expect(() => builder.build()).toThrow(
67
+ `Required "format" operation has not been set for "changeAccountSubscriber"`,
68
+ );
69
+
70
+ builder.format(garbageFormatter);
71
+ expect(() => builder.build()).toThrow(
72
+ `Required "addEventListener" operation has not been set for "changeAccountSubscriber"`,
73
+ );
74
+
75
+ builder.addEventListener(garbageAddEventListener);
76
+ expect(() => builder.build()).toThrow(
77
+ `Required "removeEventListener" operation has not been set for "changeAccountSubscriber"`,
78
+ );
79
+
80
+ builder.removeEventListener(garbageRemoveEventListener);
81
+
82
+ // should not throw now
83
+ builder.build();
84
+ });
85
+
86
+ test('calls addEventListener and formats accounts correctly', async () => {
87
+ const { context } = setupNamespace();
88
+ const spiedAddEventListener = vi.fn(garbageAddEventListener);
89
+
90
+ builder
91
+ .getInstance(() => garbageProvider)
92
+ .format(garbageFormatter)
93
+ .addEventListener(spiedAddEventListener)
94
+ .removeEventListener(garbageRemoveEventListener);
95
+
96
+ const [subscriber] = builder.build();
97
+
98
+ expect(spiedAddEventListener).toHaveBeenCalledTimes(0);
99
+ subscriber(context);
100
+ expect(spiedAddEventListener).toHaveBeenCalledTimes(1);
101
+
102
+ const [getState] = context.state();
103
+ await waitFor(() => {
104
+ expect(getState('accounts')?.[0]).toBe('Formatted: 0');
105
+ });
106
+ });
107
+
108
+ test('wont update accounts if preventDefault called', async () => {
109
+ const { context } = setupNamespace();
110
+ const spiedAddEventListener = vi.fn(garbageAddEventListener);
111
+ const [getState, setState] = context.state();
112
+ setState('accounts', ['Initial State']);
113
+ builder
114
+ .getInstance(() => garbageProvider)
115
+ .format(garbageFormatter)
116
+ .addEventListener(spiedAddEventListener)
117
+ .onSwitchAccount((event) => {
118
+ event.preventDefault();
119
+ })
120
+ .removeEventListener(garbageRemoveEventListener);
121
+
122
+ const [subscriber] = builder.build();
123
+
124
+ subscriber(context);
125
+ await waitFor(() => {
126
+ expect(getState('accounts')?.[0]).toBe('Initial State');
127
+ });
128
+ });
129
+
130
+ test('calls removeEventListener', () => {
131
+ const { context } = setupNamespace();
132
+ const spiedRemoveEventListener = vi.fn(garbageRemoveEventListener);
133
+
134
+ builder
135
+ .getInstance(() => garbageProvider)
136
+ .format(garbageFormatter)
137
+ .addEventListener(garbageAddEventListener)
138
+ .removeEventListener(spiedRemoveEventListener);
139
+
140
+ const [, clearSubscriber] = builder.build();
141
+
142
+ expect(spiedRemoveEventListener).toHaveBeenCalledTimes(0);
143
+ clearSubscriber(context);
144
+ expect(spiedRemoveEventListener).toHaveBeenCalledTimes(1);
145
+ });
146
+
147
+ test('calls addEventListener returned function', () => {
148
+ const { context } = setupNamespace();
149
+ const unsubscribe = vi.fn();
150
+
151
+ const addEventListenerWithReturn = (
152
+ _: typeof garbageProvider,
153
+ __: (event: string[]) => void,
154
+ ) => {
155
+ return unsubscribe;
156
+ };
157
+
158
+ builder
159
+ .getInstance(() => garbageProvider)
160
+ .format(garbageFormatter)
161
+ .addEventListener(addEventListenerWithReturn)
162
+ .removeEventListener(garbageRemoveEventListener);
163
+
164
+ const [subscriber, clearSubscriber] = builder.build();
165
+
166
+ subscriber(context);
167
+ expect(unsubscribe).toHaveBeenCalledTimes(0);
168
+
169
+ clearSubscriber(context);
170
+ expect(unsubscribe).toHaveBeenCalledTimes(1);
171
+ });
172
+ });
@@ -0,0 +1,239 @@
1
+ import type { AutoImplementedActionsByRecommended } from '../types/namespace.js';
2
+ import type {
3
+ Actions,
4
+ Context,
5
+ Subscriber,
6
+ SubscriberCleanUp,
7
+ } from '@hub3js/core';
8
+
9
+ type OnSwitchAccountEvent<EventType> = {
10
+ payload: EventType;
11
+ preventDefault: () => void;
12
+ };
13
+
14
+ export class ChangeAccountSubscriberBuilder<
15
+ EventType,
16
+ ProviderAPI,
17
+ ActionsType extends Actions<ActionsType> &
18
+ Actions<AutoImplementedActionsByRecommended>,
19
+ > {
20
+ #getInstance: (() => ProviderAPI) | null = null;
21
+ #format:
22
+ | ((instance: ProviderAPI, event: EventType) => Promise<string[]>)
23
+ | null = null;
24
+ #onSwitchAccount:
25
+ | ((
26
+ event: OnSwitchAccountEvent<EventType>,
27
+ context: Context<ActionsType>,
28
+ ) => void)
29
+ | null = null;
30
+
31
+ #addEventListener:
32
+ | ((
33
+ instance: ProviderAPI,
34
+ callback: (event: EventType) => void,
35
+ ) => (() => void) | void)
36
+ | null = null;
37
+ #removeEventListener:
38
+ | ((instance: ProviderAPI, callback: (event: EventType) => void) => void)
39
+ | null = null;
40
+
41
+ /**
42
+ * Sets the function that provides the provider API instance.
43
+ *
44
+ * @param operator - Function that returns the provider API instance
45
+ * @returns The builder instance for method chaining
46
+ * @example
47
+ * ```typescript
48
+ * builder.getInstance(() => window.ethereum)
49
+ * ```
50
+ */
51
+ public getInstance(operator: () => ProviderAPI) {
52
+ this.#getInstance = operator;
53
+ return this;
54
+ }
55
+
56
+ /**
57
+ * Sets the formatter function that converts provider events to account strings.
58
+ *
59
+ * @param operator - Function that takes a provider instance and event, returns array of account strings
60
+ * @returns The builder instance for method chaining
61
+ * @example
62
+ * ```typescript
63
+ * builder.format(async (instance, event) => event.accounts || [])
64
+ * ```
65
+ */
66
+ public format(
67
+ operator: (instance: ProviderAPI, event: EventType) => Promise<string[]>,
68
+ ) {
69
+ this.#format = operator;
70
+ return this;
71
+ }
72
+
73
+ /**
74
+ * Set a handler that runs whenever a **switch-account** event occurs.
75
+ *
76
+ * The provided operator is called with:
77
+ * - `event`: An object containing:
78
+ * - `payload`: The event payload associated with the account switch.
79
+ * - `preventDefault`: A function that prevents the default switch-account
80
+ * behavior (e.g., updating the active accounts list).
81
+ * - `context`: The execution context of the current flow or action.
82
+ *
83
+ * Calling `event.preventDefault()` inside the handler cancels the built-in
84
+ * account switching logic.
85
+ *
86
+ * @param operator - A function invoked on each switch-account event.
87
+ * It receives `(event, context)` and can call `event.preventDefault()` to
88
+ * stop the default behavior.
89
+ *
90
+ * @returns The builder instance for method chaining.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * builder.onSwitchAccount((event, context) => {
95
+ * if (!event.payload.userConfirmed) {
96
+ * event.preventDefault(); // cancel default account switch
97
+ * }
98
+ *
99
+ * console.log("Switching account:", event.payload.accountId);
100
+ * });
101
+ * ```
102
+ */
103
+ public onSwitchAccount(
104
+ operator: (
105
+ event: OnSwitchAccountEvent<EventType>,
106
+ context: Context<ActionsType>,
107
+ ) => void,
108
+ ) {
109
+ this.#onSwitchAccount = operator;
110
+ return this;
111
+ }
112
+
113
+ /**
114
+ * Sets the event listener attachment function.
115
+ *
116
+ * @param operator - Function that attaches an event listener and optionally returns a cleanup function
117
+ * @returns The builder instance for method chaining
118
+ * @example
119
+ * ```typescript
120
+ * builder.addEventListener((instance, callback) => {
121
+ * return instance.on('accountsChanged', callback);
122
+ * })
123
+ * ```
124
+ */
125
+ public addEventListener(
126
+ operator: (
127
+ instance: ProviderAPI,
128
+ callback: (event: EventType) => void,
129
+ ) => (() => void) | void,
130
+ ) {
131
+ this.#addEventListener = operator;
132
+ return this;
133
+ }
134
+
135
+ /**
136
+ * Sets the event listener removal function.
137
+ *
138
+ * @param operator - Function that removes an event listener from the provider
139
+ * @returns The builder instance for method chaining
140
+ * @example
141
+ * ```typescript
142
+ * builder.removeEventListener((instance, callback) => instance.off('accountsChanged', callback))
143
+ * ```
144
+ */
145
+ public removeEventListener(
146
+ operator: (
147
+ instance: ProviderAPI,
148
+ callback: (event: EventType) => void,
149
+ ) => void,
150
+ ) {
151
+ this.#removeEventListener = operator;
152
+ return this;
153
+ }
154
+
155
+ public build(): [Subscriber<ActionsType>, SubscriberCleanUp<ActionsType>] {
156
+ if (this.#getInstance === null) {
157
+ throw new Error(this.#getErrorMessage('getInstance'));
158
+ }
159
+ if (this.#format === null) {
160
+ throw new Error(this.#getErrorMessage('format'));
161
+ }
162
+ if (this.#addEventListener === null) {
163
+ throw new Error(this.#getErrorMessage('addEventListener'));
164
+ }
165
+ if (this.#removeEventListener === null) {
166
+ throw new Error(this.#getErrorMessage('removeEventListener'));
167
+ }
168
+
169
+ /*
170
+ * Capture current operator state at build time to ensure immutability.
171
+ *
172
+ * This creates a snapshot of all operators, preventing the built subscriber
173
+ * from being affected by subsequent changes to the builder instance.
174
+ * Each call to build() gets its own isolated set of operators, allowing
175
+ * the builder to be reused for creating multiple independent subscribers.
176
+ */
177
+ const getInstance = this.#getInstance;
178
+ const format = this.#format;
179
+ const addEventListener = this.#addEventListener;
180
+ const removeEventListener = this.#removeEventListener;
181
+ const onSwitchAccount = this.#onSwitchAccount;
182
+
183
+ let subscriber: (event: EventType) => void;
184
+ let unsubscribe: (() => void) | void;
185
+ return [
186
+ async (context) => {
187
+ const [, setState] = context.state();
188
+ const instance = getInstance();
189
+
190
+ if (!instance) {
191
+ throw new Error(
192
+ 'Trying to subscribe to your wallet, but seems its instance is not available.',
193
+ );
194
+ }
195
+ subscriber = async (event) => {
196
+ let shouldProceedWithDefault = true;
197
+ onSwitchAccount?.(
198
+ {
199
+ payload: event,
200
+ preventDefault: () => {
201
+ shouldProceedWithDefault = false;
202
+ },
203
+ },
204
+ context,
205
+ );
206
+ if (!shouldProceedWithDefault) {
207
+ return;
208
+ }
209
+ setState('accounts', await format(instance, event));
210
+ };
211
+ unsubscribe = addEventListener(instance, subscriber);
212
+ },
213
+ (_, err) => {
214
+ /*
215
+ * Call the cleanup function if addEventListener returned one.
216
+ * This handles providers that return an unsubscribe function from
217
+ * their event listeners.
218
+ */
219
+ if (unsubscribe && typeof unsubscribe === 'function') {
220
+ unsubscribe();
221
+ }
222
+ const instance = getInstance();
223
+
224
+ /*
225
+ * Always call removeEventListener as well to handle the on/off pattern.
226
+ * This ensures cleanup works regardless of which pattern the provider uses.
227
+ */
228
+ removeEventListener(instance, subscriber);
229
+
230
+ // subscriber can be passed to `or`; rethrow to chain into next `or`.
231
+ return err;
232
+ },
233
+ ];
234
+ }
235
+
236
+ #getErrorMessage(operatorName: string) {
237
+ return `Required "${operatorName}" operation has not been set for "changeAccountSubscriber"`;
238
+ }
239
+ }
@@ -0,0 +1 @@
1
+ export { ChangeAccountSubscriberBuilder } from './changeAccountSubscriber.js';
@@ -0,0 +1,8 @@
1
+ import type { Context } from '@hub3js/core';
2
+
3
+ export function intoConnectionFinished(context: Context) {
4
+ const [, setState] = context.state();
5
+ setState('connecting', false);
6
+ }
7
+
8
+ export const recommended = [['connect', intoConnectionFinished] as const];
@@ -0,0 +1,39 @@
1
+ import type { Accounts, AccountsWithActiveChain } from '../types/accounts.js';
2
+ import type { Context } from '@hub3js/core';
3
+
4
+ import { isValidCaipAddress } from '../utils/caip.js';
5
+
6
+ export function connectAndUpdateStateForSingleNetwork(
7
+ context: Context,
8
+ accounts: Accounts,
9
+ ) {
10
+ if (!accounts.every(isValidCaipAddress)) {
11
+ throw new Error(
12
+ `Your provider should format account addresses in CAIP-10 format. Your provided accounts: ${accounts}`,
13
+ );
14
+ }
15
+
16
+ const [, setState] = context.state();
17
+ setState('accounts', accounts);
18
+ setState('connected', true);
19
+ return accounts;
20
+ }
21
+
22
+ export function connectAndUpdateStateForMultiNetworks(
23
+ context: Context,
24
+ accounts: AccountsWithActiveChain,
25
+ ) {
26
+ if (!accounts.accounts.every(isValidCaipAddress)) {
27
+ throw new Error(
28
+ `Your provider should format account addresses in CAIP-10 format. Your provided accounts: ${accounts.accounts}`,
29
+ );
30
+ }
31
+
32
+ const [, setState] = context.state();
33
+ setState('accounts', accounts.accounts);
34
+ setState('network', accounts.network);
35
+ setState('connected', true);
36
+ return accounts;
37
+ }
38
+
39
+ export const recommended = [];
@@ -0,0 +1,12 @@
1
+ import type { Context } from '@hub3js/core';
2
+
3
+ export function intoConnecting(context: Context) {
4
+ const [, setState] = context.state();
5
+ setState('connecting', true);
6
+ }
7
+
8
+ /**
9
+ * Recommended `before` hooks every namespace should wire up. Tuple shape
10
+ * matches what `NamespaceBuilder.before(...)` expects.
11
+ */
12
+ export const recommended = [['connect', intoConnecting] as const];
@@ -0,0 +1,11 @@
1
+ export { intoConnecting, recommended as beforeRecommended } from './before.js';
2
+ export {
3
+ intoConnectionFinished,
4
+ recommended as afterRecommended,
5
+ } from './after.js';
6
+ export {
7
+ connectAndUpdateStateForMultiNetworks,
8
+ connectAndUpdateStateForSingleNetwork,
9
+ recommended as andRecommended,
10
+ } from './and.js';
11
+ export { standardizeAndThrowError } from './or.js';
@@ -0,0 +1,19 @@
1
+ import type { Context } from '@hub3js/core';
2
+
3
+ import { parseErrorAndThrowStandardizeError } from '../utils/error.js';
4
+
5
+ /**
6
+ * Standardizes an unknown error into an `Error` object and throws it. If the
7
+ * input is already an `Error`, it's thrown directly. Otherwise a new `Error`
8
+ * is created with the input's message or string representation.
9
+ *
10
+ * The actual standardization lives in `parseErrorAndThrowStandardizeError`,
11
+ * which can be called independently outside an action context.
12
+ *
13
+ * @param _context - The action context (unused).
14
+ * @param e - The unknown error object.
15
+ * @throws {Error} - The standardized `Error` object.
16
+ */
17
+ export function standardizeAndThrowError(_context: Context, e: unknown): never {
18
+ parseErrorAndThrowStandardizeError(e);
19
+ }
@@ -0,0 +1,13 @@
1
+ type CaipNamespace = string;
2
+ type CaipChainId = string;
3
+ type CaipAccountAddress = string;
4
+
5
+ export type CaipAccount =
6
+ `${CaipNamespace}:${CaipChainId}:${CaipAccountAddress}`;
7
+
8
+ export type Accounts = CaipAccount[];
9
+
10
+ export type AccountsWithActiveChain = {
11
+ accounts: Accounts;
12
+ network: string;
13
+ };
@@ -0,0 +1,11 @@
1
+ export type {
2
+ Namespace,
3
+ CommonActions,
4
+ AutoImplementedActionsByRecommended,
5
+ } from './namespace.js';
6
+
7
+ export type {
8
+ CaipAccount,
9
+ Accounts,
10
+ AccountsWithActiveChain,
11
+ } from './accounts.js';
@@ -0,0 +1,26 @@
1
+ /*
2
+ * These are the supported namespace tags. The opaque `(string & {})` widening
3
+ * keeps the union open — downstream packages can register arbitrary names —
4
+ * while preserving autocomplete for the well-known ones.
5
+ */
6
+ type KnownNamespace =
7
+ | 'EVM'
8
+ | 'Solana'
9
+ | 'Cosmos'
10
+ | 'UTXO'
11
+ | 'Starknet'
12
+ | 'Tron'
13
+ | 'Ton'
14
+ | 'Sui'
15
+ | 'XRPL'
16
+ | 'Stellar';
17
+
18
+ export type Namespace = KnownNamespace | (string & {});
19
+
20
+ export interface CommonActions {
21
+ init: () => void;
22
+ }
23
+
24
+ export interface AutoImplementedActionsByRecommended {
25
+ disconnect: () => void;
26
+ }
@@ -0,0 +1,10 @@
1
+ import { AccountId } from 'caip';
2
+
3
+ export function isValidCaipAddress(address: string): boolean {
4
+ try {
5
+ AccountId.parse(address);
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Standardizes an unknown error into an `Error` object and throws it.
3
+ * If the input is already an `Error`, it's thrown directly. Otherwise a new
4
+ * `Error` is created with the input's message or string representation,
5
+ * preserving `code` and `data` fields when present.
6
+ *
7
+ * @param e - The unknown error object.
8
+ * @throws {Error} - The standardized `Error` object.
9
+ */
10
+ export function parseErrorAndThrowStandardizeError(e: unknown): never {
11
+ if (e instanceof Error) {
12
+ throw e;
13
+ }
14
+
15
+ let errorMessage: string;
16
+ if (
17
+ typeof e === 'object' &&
18
+ e !== null &&
19
+ 'message' in e &&
20
+ typeof e.message === 'string'
21
+ ) {
22
+ errorMessage = e.message;
23
+ } else if (typeof e === 'string') {
24
+ errorMessage = e;
25
+ } else {
26
+ errorMessage = String(e);
27
+ }
28
+
29
+ const err = new Error(errorMessage) as Error & {
30
+ code?: unknown;
31
+ data?: unknown;
32
+ };
33
+
34
+ if (typeof e === 'object' && e !== null) {
35
+ if ('code' in e) {
36
+ err.code = e.code;
37
+ }
38
+ if ('data' in e) {
39
+ err.data = e.data;
40
+ }
41
+ }
42
+
43
+ throw err;
44
+ }
@@ -0,0 +1,2 @@
1
+ export { isValidCaipAddress } from './caip.js';
2
+ export { parseErrorAndThrowStandardizeError } from './error.js';
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@hub3js/std/types",
3
+ "type": "module",
4
+ "main": "../dist/types/mod.js",
5
+ "module": "../dist/types/mod.js",
6
+ "types": "../dist/types/mod.d.ts",
7
+ "sideEffects": false
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@hub3js/std/utils",
3
+ "type": "module",
4
+ "main": "../dist/utils/mod.js",
5
+ "module": "../dist/utils/mod.js",
6
+ "types": "../dist/utils/mod.d.ts",
7
+ "sideEffects": false
8
+ }