@rango-dev/wallets-core 0.38.0 → 0.38.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.
- package/CHANGELOG.md +4 -0
- package/dist/builders/action.d.ts +22 -0
- package/dist/builders/action.d.ts.map +1 -0
- package/dist/builders/mod.d.ts +5 -0
- package/dist/builders/mod.d.ts.map +1 -0
- package/dist/builders/namespace.d.ts +47 -0
- package/dist/builders/namespace.d.ts.map +1 -0
- package/dist/builders/provider.d.ts +13 -0
- package/dist/builders/provider.d.ts.map +1 -0
- package/dist/builders/types.d.ts +21 -0
- package/dist/builders/types.d.ts.map +1 -0
- package/dist/hub/helpers.d.ts +6 -0
- package/dist/hub/helpers.d.ts.map +1 -0
- package/dist/hub/hub.d.ts +28 -0
- package/dist/hub/hub.d.ts.map +1 -0
- package/dist/hub/mod.d.ts +7 -0
- package/dist/hub/mod.d.ts.map +1 -0
- package/dist/hub/namespaces/errors.d.ts +4 -0
- package/dist/hub/namespaces/errors.d.ts.map +1 -0
- package/dist/hub/namespaces/mod.d.ts +3 -0
- package/dist/hub/namespaces/mod.d.ts.map +1 -0
- package/dist/hub/namespaces/namespace.d.ts +146 -0
- package/dist/hub/namespaces/namespace.d.ts.map +1 -0
- package/dist/hub/namespaces/namespace.test.d.ts +2 -0
- package/dist/hub/namespaces/namespace.test.d.ts.map +1 -0
- package/dist/hub/namespaces/types.d.ts +33 -0
- package/dist/hub/namespaces/types.d.ts.map +1 -0
- package/dist/hub/provider/mod.d.ts +3 -0
- package/dist/hub/provider/mod.d.ts.map +1 -0
- package/dist/hub/provider/provider.d.ts +136 -0
- package/dist/hub/provider/provider.d.ts.map +1 -0
- package/dist/hub/provider/provider.test.d.ts +2 -0
- package/dist/hub/provider/provider.test.d.ts.map +1 -0
- package/dist/hub/provider/types.d.ts +28 -0
- package/dist/hub/provider/types.d.ts.map +1 -0
- package/dist/hub/store/hub.d.ts +11 -0
- package/dist/hub/store/hub.d.ts.map +1 -0
- package/dist/hub/store/mod.d.ts +6 -0
- package/dist/hub/store/mod.d.ts.map +1 -0
- package/dist/hub/store/namespaces.d.ts +34 -0
- package/dist/hub/store/namespaces.d.ts.map +1 -0
- package/dist/hub/store/providers.d.ts +45 -0
- package/dist/hub/store/providers.d.ts.map +1 -0
- package/dist/hub/store/selectors.d.ts +18 -0
- package/dist/hub/store/selectors.d.ts.map +1 -0
- package/dist/hub/store/store.d.ts +13 -0
- package/dist/hub/store/store.d.ts.map +1 -0
- package/dist/hub/store/store.test.d.ts +2 -0
- package/dist/hub/store/store.test.d.ts.map +1 -0
- package/dist/mod.d.ts +6 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -0
- package/dist/mod.js.map +4 -4
- package/dist/namespaces/common/actions.d.ts +4 -0
- package/dist/namespaces/common/actions.d.ts.map +1 -0
- package/dist/namespaces/common/after.d.ts +4 -0
- package/dist/namespaces/common/after.d.ts.map +1 -0
- package/dist/namespaces/common/and.d.ts +6 -0
- package/dist/namespaces/common/and.d.ts.map +1 -0
- package/dist/namespaces/common/before.d.ts +4 -0
- package/dist/namespaces/common/before.d.ts.map +1 -0
- package/dist/namespaces/common/builders.d.ts +4 -0
- package/dist/namespaces/common/builders.d.ts.map +1 -0
- package/dist/namespaces/common/helpers.d.ts +2 -0
- package/dist/namespaces/common/helpers.d.ts.map +1 -0
- package/dist/namespaces/common/mod.d.ts +6 -0
- package/dist/namespaces/common/mod.d.ts.map +1 -0
- package/dist/namespaces/common/mod.js +2 -0
- package/dist/namespaces/common/mod.js.map +7 -0
- package/dist/namespaces/common/types.d.ts +7 -0
- package/dist/namespaces/common/types.d.ts.map +1 -0
- package/dist/namespaces/cosmos/mod.d.ts +2 -0
- package/dist/namespaces/cosmos/mod.d.ts.map +1 -0
- package/dist/namespaces/cosmos/types.d.ts +4 -0
- package/dist/namespaces/cosmos/types.d.ts.map +1 -0
- package/dist/namespaces/evm/actions.d.ts +8 -0
- package/dist/namespaces/evm/actions.d.ts.map +1 -0
- package/dist/namespaces/evm/after.d.ts +2 -0
- package/dist/namespaces/evm/after.d.ts.map +1 -0
- package/dist/namespaces/evm/and.d.ts +3 -0
- package/dist/namespaces/evm/and.d.ts.map +1 -0
- package/dist/namespaces/evm/before.d.ts +2 -0
- package/dist/namespaces/evm/before.d.ts.map +1 -0
- package/dist/namespaces/evm/builders.d.ts +4 -0
- package/dist/namespaces/evm/builders.d.ts.map +1 -0
- package/dist/namespaces/evm/constants.d.ts +3 -0
- package/dist/namespaces/evm/constants.d.ts.map +1 -0
- package/dist/namespaces/evm/eip1193.d.ts +1228 -0
- package/dist/namespaces/evm/eip1193.d.ts.map +1 -0
- package/dist/namespaces/evm/mod.d.ts +9 -0
- package/dist/namespaces/evm/mod.d.ts.map +1 -0
- package/dist/namespaces/evm/mod.js +2 -0
- package/dist/namespaces/evm/mod.js.map +7 -0
- package/dist/namespaces/evm/types.d.ts +10 -0
- package/dist/namespaces/evm/types.d.ts.map +1 -0
- package/dist/namespaces/evm/utils.d.ts +9 -0
- package/dist/namespaces/evm/utils.d.ts.map +1 -0
- package/dist/namespaces/solana/actions.d.ts +6 -0
- package/dist/namespaces/solana/actions.d.ts.map +1 -0
- package/dist/namespaces/solana/after.d.ts +2 -0
- package/dist/namespaces/solana/after.d.ts.map +1 -0
- package/dist/namespaces/solana/and.d.ts +3 -0
- package/dist/namespaces/solana/and.d.ts.map +1 -0
- package/dist/namespaces/solana/before.d.ts +2 -0
- package/dist/namespaces/solana/before.d.ts.map +1 -0
- package/dist/namespaces/solana/builders.d.ts +4 -0
- package/dist/namespaces/solana/builders.d.ts.map +1 -0
- package/dist/namespaces/solana/constants.d.ts +3 -0
- package/dist/namespaces/solana/constants.d.ts.map +1 -0
- package/dist/namespaces/solana/mod.d.ts +8 -0
- package/dist/namespaces/solana/mod.d.ts.map +1 -0
- package/dist/namespaces/solana/mod.js +2 -0
- package/dist/namespaces/solana/mod.js.map +7 -0
- package/dist/namespaces/solana/types.d.ts +7 -0
- package/dist/namespaces/solana/types.d.ts.map +1 -0
- package/dist/test-utils/fixtures.d.ts +3 -0
- package/dist/test-utils/fixtures.d.ts.map +1 -0
- package/dist/types/accounts.d.ts +11 -0
- package/dist/types/accounts.d.ts.map +1 -0
- package/dist/types/actions.d.ts +5 -0
- package/dist/types/actions.d.ts.map +1 -0
- package/dist/types/utils.d.ts +7 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/utils/mod.d.ts +4 -0
- package/dist/utils/mod.d.ts.map +1 -0
- package/dist/utils/mod.js +2 -0
- package/dist/utils/mod.js.map +7 -0
- package/dist/utils/versions.d.ts +21 -0
- package/dist/utils/versions.d.ts.map +1 -0
- package/dist/utils/versions.test.d.ts +2 -0
- package/dist/utils/versions.test.d.ts.map +1 -0
- package/dist/wallets-core.build.json +1 -1
- package/package.json +25 -4
- package/src/builders/action.ts +86 -0
- package/src/builders/mod.ts +5 -0
- package/src/builders/namespace.ts +229 -0
- package/src/builders/provider.ts +61 -0
- package/src/builders/types.ts +29 -0
- package/src/hub/helpers.ts +11 -0
- package/src/hub/hub.ts +122 -0
- package/src/hub/mod.ts +10 -0
- package/src/hub/namespaces/errors.ts +8 -0
- package/src/hub/namespaces/mod.ts +9 -0
- package/src/hub/namespaces/namespace.test.ts +333 -0
- package/src/hub/namespaces/namespace.ts +443 -0
- package/src/hub/namespaces/types.ts +50 -0
- package/src/hub/provider/mod.ts +9 -0
- package/src/hub/provider/provider.test.ts +231 -0
- package/src/hub/provider/provider.ts +330 -0
- package/src/hub/provider/types.ts +37 -0
- package/src/hub/store/hub.ts +18 -0
- package/src/hub/store/mod.ts +8 -0
- package/src/hub/store/namespaces.ts +90 -0
- package/src/hub/store/providers.ts +97 -0
- package/src/hub/store/selectors.ts +59 -0
- package/src/hub/store/store.test.ts +32 -0
- package/src/hub/store/store.ts +26 -0
- package/src/mod.ts +35 -1
- package/src/namespaces/common/actions.ts +11 -0
- package/src/namespaces/common/after.ts +8 -0
- package/src/namespaces/common/and.ts +42 -0
- package/src/namespaces/common/before.ts +9 -0
- package/src/namespaces/common/builders.ts +19 -0
- package/src/namespaces/common/helpers.ts +10 -0
- package/src/namespaces/common/mod.ts +12 -0
- package/src/namespaces/common/types.ts +7 -0
- package/src/namespaces/cosmos/mod.ts +1 -0
- package/src/namespaces/cosmos/types.ts +10 -0
- package/src/namespaces/evm/actions.ts +97 -0
- package/src/namespaces/evm/after.ts +3 -0
- package/src/namespaces/evm/and.ts +5 -0
- package/src/namespaces/evm/before.ts +3 -0
- package/src/namespaces/evm/builders.ts +10 -0
- package/src/namespaces/evm/constants.ts +2 -0
- package/src/namespaces/evm/eip1193.ts +1414 -0
- package/src/namespaces/evm/mod.ts +9 -0
- package/src/namespaces/evm/types.ts +18 -0
- package/src/namespaces/evm/utils.ts +52 -0
- package/src/namespaces/solana/actions.ts +70 -0
- package/src/namespaces/solana/after.ts +3 -0
- package/src/namespaces/solana/and.ts +5 -0
- package/src/namespaces/solana/before.ts +3 -0
- package/src/namespaces/solana/builders.ts +10 -0
- package/src/namespaces/solana/constants.ts +2 -0
- package/src/namespaces/solana/mod.ts +8 -0
- package/src/namespaces/solana/types.ts +20 -0
- package/src/test-utils/fixtures.ts +9 -0
- package/src/types/accounts.ts +12 -0
- package/src/types/actions.ts +11 -0
- package/src/types/utils.ts +7 -0
- package/src/utils/mod.ts +8 -0
- package/src/utils/versions.test.ts +22 -0
- package/src/utils/versions.ts +63 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Actions,
|
|
3
|
+
Context,
|
|
4
|
+
GetState,
|
|
5
|
+
HooksWithOptions,
|
|
6
|
+
Operators,
|
|
7
|
+
RegisteredActions,
|
|
8
|
+
SetState,
|
|
9
|
+
State,
|
|
10
|
+
} from './types.js';
|
|
11
|
+
import type {
|
|
12
|
+
AndFunction,
|
|
13
|
+
AnyFunction,
|
|
14
|
+
FunctionWithContext,
|
|
15
|
+
} from '../../types/actions.js';
|
|
16
|
+
import type { NamespaceConfig, Store } from '../store/mod.js';
|
|
17
|
+
|
|
18
|
+
import { generateStoreId, isAsync } from '../helpers.js';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
ACTION_NOT_FOUND_ERROR,
|
|
22
|
+
NO_STORE_FOUND_ERROR,
|
|
23
|
+
OR_ELSE_ACTION_FAILED_ERROR,
|
|
24
|
+
} from './errors.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* A Namespace is a unit of wallets where usually handles connecting, signing, accounts, ...
|
|
29
|
+
* It will be injected by wallet in its object, for example, `window.phantom.ethereum` or `window.phantom.solana`
|
|
30
|
+
* Each namespace (like solana) has its own functionality which is not shared between all the blockchains.
|
|
31
|
+
* For example in EVM namespaces, you can have different networks (e.g. Ethereum,Polygon, ...) and there are specific flows to handle connecting to them or add a network and etc.
|
|
32
|
+
* But Solana doesn't have this concept and you will directly always connect to solana itself.
|
|
33
|
+
* This is true for signing a transaction, getting information about blockchain and more.
|
|
34
|
+
* So by creating a namespace for each of these, we can define a custom namespace based on blockchain's properties.
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
class Namespace<T extends Actions<T>> {
|
|
38
|
+
/** it will be used for `store` and accessing to store by its id mainly. */
|
|
39
|
+
public readonly namespaceId: string;
|
|
40
|
+
/** it will be used for `store` and accessing to store by its id mainly. */
|
|
41
|
+
public readonly providerId: string;
|
|
42
|
+
|
|
43
|
+
#actions: RegisteredActions<T>;
|
|
44
|
+
#andOperators: Operators<T> = new Map();
|
|
45
|
+
#orOperators: Operators<T> = new Map();
|
|
46
|
+
// `context` for these two can be Namespace context or Provider context
|
|
47
|
+
#beforeHooks: HooksWithOptions<T> = new Map();
|
|
48
|
+
#afterHooks: HooksWithOptions<T> = new Map();
|
|
49
|
+
|
|
50
|
+
#initiated = false;
|
|
51
|
+
#store: Store | undefined;
|
|
52
|
+
// Namespace doesn't has any configs now, but we will need the feature in future
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
54
|
+
// @ts-ignore noUnusedParameters
|
|
55
|
+
#configs: NamespaceConfig;
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
id: string,
|
|
59
|
+
providerId: string,
|
|
60
|
+
options: {
|
|
61
|
+
store?: Store;
|
|
62
|
+
configs?: NamespaceConfig;
|
|
63
|
+
actions: RegisteredActions<T>;
|
|
64
|
+
}
|
|
65
|
+
) {
|
|
66
|
+
const { configs, actions } = options;
|
|
67
|
+
|
|
68
|
+
this.namespaceId = id;
|
|
69
|
+
this.providerId = providerId;
|
|
70
|
+
|
|
71
|
+
this.#configs = configs || new Map();
|
|
72
|
+
this.#actions = actions;
|
|
73
|
+
|
|
74
|
+
if (options.store) {
|
|
75
|
+
this.store(options.store);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* This is an special action that will be called **only once**.
|
|
81
|
+
* We don't call this in `constructor` and developer should call this manually. we only ensure it will be called once.
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* const myInit = () => { whatever; }
|
|
85
|
+
* const actions = new Map();
|
|
86
|
+
* actions.set("init", myInit)
|
|
87
|
+
* const ns = new Namespace(..., {actions});
|
|
88
|
+
*
|
|
89
|
+
* // Will run `myInit`
|
|
90
|
+
* ns.init()
|
|
91
|
+
*
|
|
92
|
+
* // Will not run `myInit` anymore.
|
|
93
|
+
* ns.init()
|
|
94
|
+
* ns.init()
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
public init(): void {
|
|
98
|
+
if (this.#initiated) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const definedInitByUser = this.#actions.get('init');
|
|
103
|
+
|
|
104
|
+
if (definedInitByUser) {
|
|
105
|
+
definedInitByUser(this.#context());
|
|
106
|
+
}
|
|
107
|
+
// else, this namespace doesn't have any `init` implemented.
|
|
108
|
+
|
|
109
|
+
this.#initiated = true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Reading states from store and also update them.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* const ns = new Namespace(...);
|
|
118
|
+
* const [getState, setState] = ns.state();
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
public state(): [GetState, SetState] {
|
|
122
|
+
const store = this.#store;
|
|
123
|
+
if (!store) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
'You need to set your store using `.store` method first.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const id = this.#storeId();
|
|
130
|
+
const setState: SetState = (name, value) => {
|
|
131
|
+
store.getState().namespaces.updateStatus(id, name, value);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const getState: GetState = <K extends keyof State>(name?: K) => {
|
|
135
|
+
const state: State = store.getState().namespaces.getNamespaceData(id);
|
|
136
|
+
|
|
137
|
+
if (!name) {
|
|
138
|
+
return state;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return state[name];
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return [getState, setState];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* For keeping state, we need a store. all the states will be write to/read from store.
|
|
149
|
+
*
|
|
150
|
+
* Note: Store can be setup on constructor as well.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const myStore = createStore();
|
|
155
|
+
* const ns = new Namespace(...);
|
|
156
|
+
* ns.store(myStore);
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
public store(store: Store): this {
|
|
160
|
+
if (this.#store) {
|
|
161
|
+
console.warn(
|
|
162
|
+
"You've already set an store for your Namespace. Old store will be replaced by the new one."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
this.#store = store;
|
|
166
|
+
this.#setupStore();
|
|
167
|
+
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* It's a boolean operator to run a sync function if action ran successfully.
|
|
173
|
+
* For example, if we have a `connect` action, we can add function to be run after `connect` if it ran successfully.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const ns = new Namespace(..);
|
|
178
|
+
*
|
|
179
|
+
* ns.and_then('connect', (context) => {
|
|
180
|
+
* ...
|
|
181
|
+
* });
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
184
|
+
*/
|
|
185
|
+
public and_then<K extends keyof T>(
|
|
186
|
+
actionName: K,
|
|
187
|
+
operatorFn: FunctionWithContext<AndFunction<T, K>, Context>
|
|
188
|
+
): this {
|
|
189
|
+
const currentAndOperators = this.#andOperators.get(actionName) || [];
|
|
190
|
+
this.#andOperators.set(actionName, currentAndOperators.concat(operatorFn));
|
|
191
|
+
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* It's a boolean operator to run a function to handle when an action fails.
|
|
197
|
+
* For example, if we have a `connect` action, we can add function to be run when `connect` fails (throw an error).
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* const ns = new Namespace(..);
|
|
202
|
+
*
|
|
203
|
+
* ns.or_else('connect', (context, error) => {
|
|
204
|
+
* ...
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
public or_else<K extends keyof T>(
|
|
209
|
+
actionName: K,
|
|
210
|
+
operatorFn: FunctionWithContext<AnyFunction, Context>
|
|
211
|
+
): this {
|
|
212
|
+
const currentOrOperators = this.#orOperators.get(actionName) || [];
|
|
213
|
+
this.#orOperators.set(actionName, currentOrOperators.concat(operatorFn));
|
|
214
|
+
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Running a function after a specific action
|
|
220
|
+
*
|
|
221
|
+
* Note: the context can be set from outside as well. this is useful for Provider to set its context instead of namespace context.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* const ns = new Namespace(...);
|
|
226
|
+
*
|
|
227
|
+
* ns.after("connect", (context) => {});
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
public after<K extends keyof T, C = unknown>(
|
|
231
|
+
actionName: K,
|
|
232
|
+
hook: FunctionWithContext<AnyFunction, C>,
|
|
233
|
+
options?: { context?: C }
|
|
234
|
+
): this {
|
|
235
|
+
const currentAfterHooks = this.#afterHooks.get(actionName) || [];
|
|
236
|
+
const hookWithOptions = {
|
|
237
|
+
hook,
|
|
238
|
+
options: {
|
|
239
|
+
context: options?.context,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
this.#afterHooks.set(actionName, currentAfterHooks.concat(hookWithOptions));
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Running a function before a specific action
|
|
249
|
+
*
|
|
250
|
+
* Note: the context can be set from outside as well. this is useful for Provider to set its context instead of using namespace context.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const ns = new Namespace(...);
|
|
255
|
+
*
|
|
256
|
+
* ns.before("connect", (context) => {});
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
public before<K extends keyof T, C = unknown>(
|
|
260
|
+
actionName: K,
|
|
261
|
+
hook: FunctionWithContext<AnyFunction, C>,
|
|
262
|
+
options?: { context?: C }
|
|
263
|
+
): this {
|
|
264
|
+
const currentBeforeHooks = this.#beforeHooks.get(actionName) || [];
|
|
265
|
+
const hookWithOptions = {
|
|
266
|
+
hook,
|
|
267
|
+
options: {
|
|
268
|
+
context: options?.context,
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
this.#beforeHooks.set(
|
|
272
|
+
actionName,
|
|
273
|
+
currentBeforeHooks.concat(hookWithOptions)
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
*
|
|
281
|
+
* Registered actions will be called using `run`. it will run an action and all the operators or hooks that assigned.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* const actions = new Map();
|
|
286
|
+
* actions.set('connect', connectAction);
|
|
287
|
+
*
|
|
288
|
+
* const ns = new Namespace(NAMESPACE, PROVIDER_ID, {
|
|
289
|
+
* actions: actions,
|
|
290
|
+
* });
|
|
291
|
+
*
|
|
292
|
+
* ns.run("action");
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
public run<K extends keyof T>(
|
|
296
|
+
actionName: K,
|
|
297
|
+
...args: any[]
|
|
298
|
+
): unknown | Promise<unknown> {
|
|
299
|
+
const action = this.#actions.get(actionName);
|
|
300
|
+
if (!action) {
|
|
301
|
+
throw new Error(ACTION_NOT_FOUND_ERROR(actionName.toString()));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
* Action can be both, sync or async. To simplify the process we can not make `sync` mode to async
|
|
306
|
+
* Since every user's sync action will be an async function and affect what user expect,
|
|
307
|
+
* it makes all the actions async and it doesn't match with Namespace interface (e.g. EvmActions)
|
|
308
|
+
*
|
|
309
|
+
* To avoid this issue and also not duplicating code, I broke the process into smaller methods
|
|
310
|
+
* and two main methods to run actions: tryRunAsyncAction & tryRunAction.
|
|
311
|
+
*/
|
|
312
|
+
const result = isAsync(action)
|
|
313
|
+
? this.#tryRunAsyncAction(actionName, args)
|
|
314
|
+
: this.#tryRunAction(actionName, args);
|
|
315
|
+
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
#tryRunAction<K extends keyof T>(actionName: K, params: any[]): unknown {
|
|
320
|
+
this.#tryRunBeforeHooks(actionName);
|
|
321
|
+
|
|
322
|
+
const action = this.#actions.get(actionName);
|
|
323
|
+
if (!action) {
|
|
324
|
+
throw new Error(ACTION_NOT_FOUND_ERROR(actionName.toString()));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const context = this.#context();
|
|
328
|
+
|
|
329
|
+
let result;
|
|
330
|
+
try {
|
|
331
|
+
result = action(context, ...params);
|
|
332
|
+
result = this.#tryRunAndOperators(actionName, result);
|
|
333
|
+
} catch (e) {
|
|
334
|
+
result = this.#tryRunOrOperators(actionName, e);
|
|
335
|
+
} finally {
|
|
336
|
+
this.#tryRunAfterHooks(actionName);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async #tryRunAsyncAction<K extends keyof T>(
|
|
343
|
+
actionName: K,
|
|
344
|
+
params: any[]
|
|
345
|
+
): Promise<unknown> {
|
|
346
|
+
this.#tryRunBeforeHooks(actionName);
|
|
347
|
+
|
|
348
|
+
const action = this.#actions.get(actionName);
|
|
349
|
+
if (!action) {
|
|
350
|
+
throw new Error(ACTION_NOT_FOUND_ERROR(actionName.toString()));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const context = this.#context();
|
|
354
|
+
return await action(context, ...params)
|
|
355
|
+
.then((result: unknown) => this.#tryRunAndOperators(actionName, result))
|
|
356
|
+
.catch((e: unknown) => this.#tryRunOrOperators(actionName, e))
|
|
357
|
+
.finally(() => this.#tryRunAfterHooks(actionName));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#tryRunAfterHooks<K extends keyof T>(actionName: K) {
|
|
361
|
+
const afterActions = this.#afterHooks.get(actionName);
|
|
362
|
+
|
|
363
|
+
if (afterActions) {
|
|
364
|
+
afterActions.forEach((afterAction) => {
|
|
365
|
+
const context = afterAction.options?.context || this.#context();
|
|
366
|
+
afterAction.hook(context);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
#tryRunBeforeHooks<K extends keyof T>(actionName: K): void {
|
|
372
|
+
const beforeActions = this.#beforeHooks.get(actionName);
|
|
373
|
+
if (beforeActions) {
|
|
374
|
+
beforeActions.forEach((beforeAction) => {
|
|
375
|
+
const context = beforeAction.options?.context || this.#context();
|
|
376
|
+
beforeAction.hook(context);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#tryRunAndOperators<K extends keyof T>(
|
|
382
|
+
actionName: K,
|
|
383
|
+
result: unknown
|
|
384
|
+
): unknown {
|
|
385
|
+
const andActions = this.#andOperators.get(actionName);
|
|
386
|
+
|
|
387
|
+
if (andActions) {
|
|
388
|
+
const context = this.#context();
|
|
389
|
+
result = andActions.reduce((prev, andAction) => {
|
|
390
|
+
return andAction(context, prev);
|
|
391
|
+
}, result);
|
|
392
|
+
}
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
#tryRunOrOperators<K extends keyof T>(
|
|
397
|
+
actionName: K,
|
|
398
|
+
actionError: unknown
|
|
399
|
+
): unknown {
|
|
400
|
+
const orActions = this.#orOperators.get(actionName);
|
|
401
|
+
|
|
402
|
+
if (orActions) {
|
|
403
|
+
try {
|
|
404
|
+
const context = this.#context();
|
|
405
|
+
return orActions.reduce((prev, orAction) => {
|
|
406
|
+
return orAction(context, prev);
|
|
407
|
+
}, actionError);
|
|
408
|
+
} catch (orError) {
|
|
409
|
+
throw new Error(OR_ELSE_ACTION_FAILED_ERROR(actionName.toString()), {
|
|
410
|
+
cause: actionError,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
throw actionError;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
#setupStore(): void {
|
|
419
|
+
const store = this.#store;
|
|
420
|
+
if (!store) {
|
|
421
|
+
throw new Error(NO_STORE_FOUND_ERROR);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const id = this.#storeId();
|
|
425
|
+
store.getState().namespaces.addNamespace(id, {
|
|
426
|
+
namespaceId: this.namespaceId,
|
|
427
|
+
providerId: this.providerId,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
#storeId() {
|
|
432
|
+
return generateStoreId(this.providerId, this.namespaceId);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
#context(): Context<T> {
|
|
436
|
+
return {
|
|
437
|
+
state: this.state.bind(this),
|
|
438
|
+
action: this.run.bind(this),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export { Namespace };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AnyFunction, FunctionWithContext } from '../../types/actions.js';
|
|
2
|
+
import type { NamespaceData } from '../store/mod.js';
|
|
3
|
+
|
|
4
|
+
type ActionName<K> = K | Omit<K, string>;
|
|
5
|
+
|
|
6
|
+
export type Subscriber<C extends Actions<C>> = (
|
|
7
|
+
context: Context<C>,
|
|
8
|
+
...args: any[]
|
|
9
|
+
) => void;
|
|
10
|
+
export type SubscriberCleanUp<C extends Actions<C>> = (
|
|
11
|
+
context: Context<C>,
|
|
12
|
+
...args: any[]
|
|
13
|
+
) => void;
|
|
14
|
+
export type State = NamespaceData;
|
|
15
|
+
export type SetState = <K extends keyof State>(
|
|
16
|
+
name: K,
|
|
17
|
+
value: State[K]
|
|
18
|
+
) => void;
|
|
19
|
+
export type GetState = {
|
|
20
|
+
(): State;
|
|
21
|
+
<K extends keyof State>(name: K): State[K];
|
|
22
|
+
};
|
|
23
|
+
export type RegisteredActions<T extends Actions<T>> = Map<
|
|
24
|
+
ActionName<keyof T>,
|
|
25
|
+
FunctionWithContext<T[keyof T], Context<T>>
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
export type AndUseActions<T> = Map<keyof T, AnyFunction>;
|
|
29
|
+
export type Operators<T> = Map<keyof T, AnyFunction[]>;
|
|
30
|
+
export type HooksWithOptions<T> = Map<
|
|
31
|
+
keyof T,
|
|
32
|
+
{
|
|
33
|
+
hook: AnyFunction;
|
|
34
|
+
options?: {
|
|
35
|
+
context?: unknown;
|
|
36
|
+
};
|
|
37
|
+
}[]
|
|
38
|
+
>;
|
|
39
|
+
export type Context<T extends Actions<T> = object> = {
|
|
40
|
+
state: () => [GetState, SetState];
|
|
41
|
+
action: (name: keyof T, ...args: any[]) => any;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* This actually define what kind of action will be implemented in namespaces.
|
|
46
|
+
* For example evm namespace will have .connect(chain: string) and .switchNetwork
|
|
47
|
+
* But solana namespace only have: `.connect()`.
|
|
48
|
+
* This actions will be passed to this generic.
|
|
49
|
+
*/
|
|
50
|
+
export type Actions<T> = Record<keyof T, AnyFunction>;
|