@subwallet/extension-dapp 0.3.6-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 (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +3 -0
  3. package/build/LICENSE +201 -0
  4. package/build/README.md +3 -0
  5. package/build/bundle-polkadot-extension-dapp.js +218 -0
  6. package/build/bundle.d.ts +13 -0
  7. package/build/bundle.js +222 -0
  8. package/build/cjs/bundle.js +297 -0
  9. package/build/cjs/detectOther.js +13 -0
  10. package/build/cjs/detectPackage.js +14 -0
  11. package/build/cjs/index.js +18 -0
  12. package/build/cjs/package.json +3 -0
  13. package/build/cjs/packageInfo.js +16 -0
  14. package/build/cjs/util.js +18 -0
  15. package/build/cjs/wrapBytes.js +23 -0
  16. package/build/detectOther.d.ts +7 -0
  17. package/build/detectOther.js +4 -0
  18. package/build/detectPackage.d.ts +1 -0
  19. package/build/detectPackage.js +7 -0
  20. package/build/index.d.ts +1 -0
  21. package/build/index.js +5 -0
  22. package/build/package.json +80 -0
  23. package/build/packageInfo.d.ts +6 -0
  24. package/build/packageInfo.js +9 -0
  25. package/build/util.d.ts +1 -0
  26. package/build/util.js +11 -0
  27. package/build/wrapBytes.d.ts +7 -0
  28. package/build/wrapBytes.js +9 -0
  29. package/build-cjs/bundle.js +297 -0
  30. package/build-cjs/detectOther.js +13 -0
  31. package/build-cjs/detectPackage.js +14 -0
  32. package/build-cjs/index.js +18 -0
  33. package/build-cjs/packageInfo.js +16 -0
  34. package/build-cjs/util.js +18 -0
  35. package/build-cjs/wrapBytes.js +23 -0
  36. package/package.json +33 -0
  37. package/src/bundle.ts +244 -0
  38. package/src/detectOther.ts +6 -0
  39. package/src/detectPackage.ts +11 -0
  40. package/src/index.ts +7 -0
  41. package/src/packageInfo.ts +6 -0
  42. package/src/util.ts +12 -0
  43. package/src/wrapBytes.spec.ts +135 -0
  44. package/src/wrapBytes.ts +12 -0
  45. package/tsconfig.build.json +12 -0
  46. package/tsconfig.build.tsbuildinfo +1 -0
  47. package/tsconfig.json +11 -0
@@ -0,0 +1,80 @@
1
+ {
2
+ "author": "Jaco Greeff <jacogr@gmail.com>",
3
+ "bugs": "https://github.com/Koniverse/Subwallet-V2/issues",
4
+ "contributors": [],
5
+ "description": "Provides an interfaces around the injected globals for ease of access by dapp developers.",
6
+ "homepage": "https://github.com/Koniverse/Subwallet-V2/tree/master/packages/extension-dapp#readme",
7
+ "license": "Apache-2.0",
8
+ "maintainers": [],
9
+ "name": "@subwallet/extension-dapp",
10
+ "repository": {
11
+ "directory": "packages/extension-dapp",
12
+ "type": "git",
13
+ "url": "https://github.com/Koniverse/Subwallet-V2.git"
14
+ },
15
+ "sideEffects": [
16
+ "./detectPackage.js",
17
+ "./cjs/detectPackage.js"
18
+ ],
19
+ "type": "module",
20
+ "version": "0.3.6-0",
21
+ "main": "./cjs/index.js",
22
+ "module": "./index.js",
23
+ "types": "./index.d.ts",
24
+ "exports": {
25
+ "./cjs/package.json": "./cjs/package.json",
26
+ "./cjs/*": "./cjs/*.js",
27
+ ".": {
28
+ "types": "./index.d.ts",
29
+ "require": "./cjs/index.js",
30
+ "default": "./index.js"
31
+ },
32
+ "./bundle": {
33
+ "types": "./bundle.d.ts",
34
+ "require": "./cjs/bundle.js",
35
+ "default": "./bundle.js"
36
+ },
37
+ "./detectOther": {
38
+ "types": "./detectOther.d.ts",
39
+ "require": "./cjs/detectOther.js",
40
+ "default": "./detectOther.js"
41
+ },
42
+ "./detectPackage": {
43
+ "types": "./detectPackage.d.ts",
44
+ "require": "./cjs/detectPackage.js",
45
+ "default": "./detectPackage.js"
46
+ },
47
+ "./package.json": "./package.json",
48
+ "./packageInfo.js": {
49
+ "types": "./packageInfo.d.ts",
50
+ "require": "./cjs/packageInfo.js",
51
+ "default": "./packageInfo.js"
52
+ },
53
+ "./packageInfo": {
54
+ "types": "./packageInfo.d.ts",
55
+ "require": "./cjs/packageInfo.js",
56
+ "default": "./packageInfo.js"
57
+ },
58
+ "./util": {
59
+ "types": "./util.d.ts",
60
+ "require": "./cjs/util.js",
61
+ "default": "./util.js"
62
+ },
63
+ "./wrapBytes": {
64
+ "types": "./wrapBytes.d.ts",
65
+ "require": "./cjs/wrapBytes.js",
66
+ "default": "./wrapBytes.js"
67
+ }
68
+ },
69
+ "dependencies": {
70
+ "@babel/runtime": "^7.16.7",
71
+ "@polkadot/util": "^8.3.1",
72
+ "@polkadot/util-crypto": "^8.3.1",
73
+ "@subwallet/extension-inject": "^0.3.6-0"
74
+ },
75
+ "peerDependencies": {
76
+ "@polkadot/api": "*",
77
+ "@polkadot/util": "*",
78
+ "@polkadot/util-crypto": "*"
79
+ }
80
+ }
@@ -0,0 +1,6 @@
1
+ export declare const packageInfo: {
2
+ name: string;
3
+ path: string;
4
+ type: string;
5
+ version: string;
6
+ };
@@ -0,0 +1,9 @@
1
+ // Copyright 2017-2022 @subwallet/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // Do not edit, auto-generated by @polkadot/dev
4
+ export const packageInfo = {
5
+ name: '@subwallet/extension-dapp',
6
+ path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
7
+ type: 'esm',
8
+ version: '0.3.6-0'
9
+ };
@@ -0,0 +1 @@
1
+ export declare function documentReadyPromise<T>(creator: () => Promise<T>): Promise<T>;
package/build/util.js ADDED
@@ -0,0 +1,11 @@
1
+ // Copyright 2019-2022 @subwallet/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export function documentReadyPromise(creator) {
4
+ return new Promise(resolve => {
5
+ if (document.readyState === 'complete') {
6
+ resolve(creator());
7
+ } else {
8
+ window.addEventListener('load', () => resolve(creator()));
9
+ }
10
+ });
11
+ }
@@ -0,0 +1,7 @@
1
+ import { u8aIsWrapped, u8aUnwrapBytes, u8aWrapBytes } from '@polkadot/util';
2
+ export declare const ETHEREUM: Uint8Array;
3
+ export declare const POSTFIX: Uint8Array;
4
+ export declare const PREFIX: Uint8Array;
5
+ export declare const isWrapped: typeof u8aIsWrapped;
6
+ export declare const unwrapBytes: typeof u8aUnwrapBytes;
7
+ export declare const wrapBytes: typeof u8aWrapBytes;
@@ -0,0 +1,9 @@
1
+ // Copyright 2019-2022 @subwallet/extension authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { U8A_WRAP_ETHEREUM, U8A_WRAP_POSTFIX, U8A_WRAP_PREFIX, u8aIsWrapped, u8aUnwrapBytes, u8aWrapBytes } from '@polkadot/util';
4
+ export const ETHEREUM = U8A_WRAP_ETHEREUM;
5
+ export const POSTFIX = U8A_WRAP_POSTFIX;
6
+ export const PREFIX = U8A_WRAP_PREFIX;
7
+ export const isWrapped = u8aIsWrapped;
8
+ export const unwrapBytes = u8aUnwrapBytes;
9
+ export const wrapBytes = u8aWrapBytes;
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isWeb3Injected = void 0;
7
+ Object.defineProperty(exports, "packageInfo", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _packageInfo.packageInfo;
11
+ }
12
+ });
13
+ Object.defineProperty(exports, "unwrapBytes", {
14
+ enumerable: true,
15
+ get: function () {
16
+ return _wrapBytes.unwrapBytes;
17
+ }
18
+ });
19
+ exports.web3Accounts = web3Accounts;
20
+ exports.web3AccountsSubscribe = web3AccountsSubscribe;
21
+ exports.web3Enable = web3Enable;
22
+ exports.web3EnablePromise = void 0;
23
+ exports.web3FromAddress = web3FromAddress;
24
+ exports.web3FromSource = web3FromSource;
25
+ exports.web3ListRpcProviders = web3ListRpcProviders;
26
+ exports.web3UseRpcProvider = web3UseRpcProvider;
27
+ Object.defineProperty(exports, "wrapBytes", {
28
+ enumerable: true,
29
+ get: function () {
30
+ return _wrapBytes.wrapBytes;
31
+ }
32
+ });
33
+
34
+ var _util = require("@polkadot/util");
35
+
36
+ var _utilCrypto = require("@polkadot/util-crypto");
37
+
38
+ var _util2 = require("./util");
39
+
40
+ var _packageInfo = require("./packageInfo");
41
+
42
+ var _wrapBytes = require("./wrapBytes");
43
+
44
+ // Copyright 2019-2022 @subwallet/extension-dapp authors & contributors
45
+ // SPDX-License-Identifier: Apache-2.0
46
+ // expose utility functions
47
+ // just a helper (otherwise we cast all-over, so shorter and more readable)
48
+ const win = window; // don't clobber the existing object, but ensure non-undefined
49
+
50
+ win.injectedWeb3 = win.injectedWeb3 || {}; // true when anything has been injected and is available
51
+
52
+ function web3IsInjected() {
53
+ return Object.keys(win.injectedWeb3).length !== 0;
54
+ } // helper to throw a consistent error when not enabled
55
+
56
+
57
+ function throwError(method) {
58
+ throw new Error(`${method}: web3Enable(originName) needs to be called before ${method}`);
59
+ } // internal helper to map from Array<InjectedAccount> -> Array<InjectedAccountWithMeta>
60
+
61
+
62
+ function mapAccounts(source, list, ss58Format) {
63
+ return list.map(_ref => {
64
+ let {
65
+ address,
66
+ genesisHash,
67
+ name,
68
+ type
69
+ } = _ref;
70
+ const encodedAddress = address.length === 42 ? address : (0, _utilCrypto.encodeAddress)((0, _utilCrypto.decodeAddress)(address), ss58Format);
71
+ return {
72
+ address: encodedAddress,
73
+ meta: {
74
+ genesisHash,
75
+ name,
76
+ source
77
+ },
78
+ type
79
+ };
80
+ });
81
+ } // have we found a properly constructed window.injectedWeb3
82
+
83
+
84
+ let isWeb3Injected = web3IsInjected(); // we keep the last promise created around (for queries)
85
+
86
+ exports.isWeb3Injected = isWeb3Injected;
87
+ let web3EnablePromise = null;
88
+ exports.web3EnablePromise = web3EnablePromise;
89
+
90
+ function getWindowExtensions(originName) {
91
+ return Promise.all(Object.entries(win.injectedWeb3).map(_ref2 => {
92
+ let [name, {
93
+ enable,
94
+ version
95
+ }] = _ref2;
96
+ return Promise.all([Promise.resolve({
97
+ name,
98
+ version
99
+ }), enable(originName).catch(error => {
100
+ console.error(`Error initializing ${name}: ${error.message}`);
101
+ })]);
102
+ }));
103
+ } // enables all the providers found on the injected window interface
104
+
105
+
106
+ function web3Enable(originName) {
107
+ let compatInits = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
108
+
109
+ if (!originName) {
110
+ throw new Error('You must pass a name for your app to the web3Enable function');
111
+ }
112
+
113
+ const initCompat = compatInits.length ? Promise.all(compatInits.map(c => c().catch(() => false))) : Promise.resolve([true]);
114
+ exports.web3EnablePromise = web3EnablePromise = (0, _util2.documentReadyPromise)(() => initCompat.then(() => getWindowExtensions(originName).then(values => values.filter(value => !!value[1]).map(_ref3 => {
115
+ let [info, ext] = _ref3;
116
+
117
+ // if we don't have an accounts subscriber, add a single-shot version
118
+ if (!ext.accounts.subscribe) {
119
+ ext.accounts.subscribe = cb => {
120
+ ext.accounts.get().then(cb).catch(console.error);
121
+ return () => {// no ubsubscribe needed, this is a single-shot
122
+ };
123
+ };
124
+ }
125
+
126
+ return { ...info,
127
+ ...ext
128
+ };
129
+ })).catch(() => []).then(values => {
130
+ const names = values.map(_ref4 => {
131
+ let {
132
+ name,
133
+ version
134
+ } = _ref4;
135
+ return `${name}/${version}`;
136
+ });
137
+ exports.isWeb3Injected = isWeb3Injected = web3IsInjected();
138
+ console.log(`web3Enable: Enabled ${values.length} extension${values.length !== 1 ? 's' : ''}: ${names.join(', ')}`);
139
+ return values;
140
+ })));
141
+ return web3EnablePromise;
142
+ } // retrieve all the accounts across all providers
143
+
144
+
145
+ async function web3Accounts() {
146
+ let {
147
+ accountType,
148
+ ss58Format
149
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
150
+
151
+ if (!web3EnablePromise) {
152
+ return throwError('web3Accounts');
153
+ }
154
+
155
+ const accounts = [];
156
+ const injected = await web3EnablePromise;
157
+ const retrieved = await Promise.all(injected.map(async _ref5 => {
158
+ let {
159
+ accounts,
160
+ name: source
161
+ } = _ref5;
162
+
163
+ try {
164
+ const list = await accounts.get();
165
+ return mapAccounts(source, list.filter(_ref6 => {
166
+ let {
167
+ type
168
+ } = _ref6;
169
+ return type && accountType ? accountType.includes(type) : true;
170
+ }), ss58Format);
171
+ } catch (error) {
172
+ // cannot handle this one
173
+ return [];
174
+ }
175
+ }));
176
+ retrieved.forEach(result => {
177
+ accounts.push(...result);
178
+ });
179
+ const addresses = accounts.map(_ref7 => {
180
+ let {
181
+ address
182
+ } = _ref7;
183
+ return address;
184
+ });
185
+ console.log(`web3Accounts: Found ${accounts.length} address${accounts.length !== 1 ? 'es' : ''}: ${addresses.join(', ')}`);
186
+ return accounts;
187
+ }
188
+
189
+ async function web3AccountsSubscribe(cb) {
190
+ let {
191
+ ss58Format
192
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
193
+
194
+ if (!web3EnablePromise) {
195
+ return throwError('web3AccountsSubscribe');
196
+ }
197
+
198
+ const accounts = {};
199
+
200
+ const triggerUpdate = () => cb(Object.entries(accounts).reduce((result, _ref8) => {
201
+ let [source, list] = _ref8;
202
+ result.push(...mapAccounts(source, list, ss58Format));
203
+ return result;
204
+ }, []));
205
+
206
+ const unsubs = (await web3EnablePromise).map(_ref9 => {
207
+ let {
208
+ accounts: {
209
+ subscribe
210
+ },
211
+ name: source
212
+ } = _ref9;
213
+ return subscribe(result => {
214
+ accounts[source] = result; // eslint-disable-next-line @typescript-eslint/no-floating-promises
215
+
216
+ triggerUpdate();
217
+ });
218
+ });
219
+ return () => {
220
+ unsubs.forEach(unsub => {
221
+ unsub();
222
+ });
223
+ };
224
+ } // find a specific provider based on the name
225
+
226
+
227
+ async function web3FromSource(source) {
228
+ if (!web3EnablePromise) {
229
+ return throwError('web3FromSource');
230
+ }
231
+
232
+ const sources = await web3EnablePromise;
233
+ const found = source && sources.find(_ref10 => {
234
+ let {
235
+ name
236
+ } = _ref10;
237
+ return name === source;
238
+ });
239
+
240
+ if (!found) {
241
+ throw new Error(`web3FromSource: Unable to find an injected ${source}`);
242
+ }
243
+
244
+ return found;
245
+ } // find a specific provider based on an address
246
+
247
+
248
+ async function web3FromAddress(address) {
249
+ if (!web3EnablePromise) {
250
+ return throwError('web3FromAddress');
251
+ }
252
+
253
+ const accounts = await web3Accounts();
254
+ let found;
255
+
256
+ if (address) {
257
+ const accountU8a = (0, _utilCrypto.decodeAddress)(address);
258
+ found = accounts.find(account => (0, _util.u8aEq)((0, _utilCrypto.decodeAddress)(account.address), accountU8a));
259
+ }
260
+
261
+ if (!found) {
262
+ throw new Error(`web3FromAddress: Unable to find injected ${address}`);
263
+ }
264
+
265
+ return web3FromSource(found.meta.source);
266
+ } // retrieve all providers exposed by one source
267
+
268
+
269
+ async function web3ListRpcProviders(source) {
270
+ const {
271
+ provider
272
+ } = await web3FromSource(source);
273
+
274
+ if (!provider) {
275
+ console.warn(`Extension ${source} does not expose any provider`);
276
+ return null;
277
+ }
278
+
279
+ return provider.listProviders();
280
+ } // retrieve all providers exposed by one source
281
+
282
+
283
+ async function web3UseRpcProvider(source, key) {
284
+ const {
285
+ provider
286
+ } = await web3FromSource(source);
287
+
288
+ if (!provider) {
289
+ throw new Error(`Extension ${source} does not expose any provider`);
290
+ }
291
+
292
+ const meta = await provider.startProvider(key);
293
+ return {
294
+ meta,
295
+ provider
296
+ };
297
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _packageInfo = require("@subwallet/extension-inject/cjs/packageInfo");
9
+
10
+ // Copyright 2017-2022 @subwallet/extension-dapp authors & contributors
11
+ // SPDX-License-Identifier: Apache-2.0
12
+ var _default = [_packageInfo.packageInfo];
13
+ exports.default = _default;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ var _util = require("@polkadot/util");
6
+
7
+ var _detectOther = _interopRequireDefault(require("./detectOther"));
8
+
9
+ var _packageInfo = require("./packageInfo");
10
+
11
+ // Copyright 2017-2022 @subwallet/extension-dapp authors & contributors
12
+ // SPDX-License-Identifier: Apache-2.0
13
+ // Do not edit, auto-generated by @polkadot/dev
14
+ (0, _util.detectPackage)(_packageInfo.packageInfo, null, _detectOther.default);
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+
7
+ var _bundle = require("./bundle");
8
+
9
+ Object.keys(_bundle).forEach(function (key) {
10
+ if (key === "default" || key === "__esModule") return;
11
+ if (key in exports && exports[key] === _bundle[key]) return;
12
+ Object.defineProperty(exports, key, {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _bundle[key];
16
+ }
17
+ });
18
+ });
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.packageInfo = void 0;
7
+ // Copyright 2017-2022 @subwallet/extension-dapp authors & contributors
8
+ // SPDX-License-Identifier: Apache-2.0
9
+ // Do not edit, auto-generated by @polkadot/dev
10
+ const packageInfo = {
11
+ name: '@subwallet/extension-dapp',
12
+ path: typeof __dirname === 'string' ? __dirname : 'auto',
13
+ type: 'cjs',
14
+ version: '0.3.6-0'
15
+ };
16
+ exports.packageInfo = packageInfo;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.documentReadyPromise = documentReadyPromise;
7
+
8
+ // Copyright 2019-2022 @subwallet/extension-dapp authors & contributors
9
+ // SPDX-License-Identifier: Apache-2.0
10
+ function documentReadyPromise(creator) {
11
+ return new Promise(resolve => {
12
+ if (document.readyState === 'complete') {
13
+ resolve(creator());
14
+ } else {
15
+ window.addEventListener('load', () => resolve(creator()));
16
+ }
17
+ });
18
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.wrapBytes = exports.unwrapBytes = exports.isWrapped = exports.PREFIX = exports.POSTFIX = exports.ETHEREUM = void 0;
7
+
8
+ var _util = require("@polkadot/util");
9
+
10
+ // Copyright 2019-2022 @subwallet/extension authors & contributors
11
+ // SPDX-License-Identifier: Apache-2.0
12
+ const ETHEREUM = _util.U8A_WRAP_ETHEREUM;
13
+ exports.ETHEREUM = ETHEREUM;
14
+ const POSTFIX = _util.U8A_WRAP_POSTFIX;
15
+ exports.POSTFIX = POSTFIX;
16
+ const PREFIX = _util.U8A_WRAP_PREFIX;
17
+ exports.PREFIX = PREFIX;
18
+ const isWrapped = _util.u8aIsWrapped;
19
+ exports.isWrapped = isWrapped;
20
+ const unwrapBytes = _util.u8aUnwrapBytes;
21
+ exports.unwrapBytes = unwrapBytes;
22
+ const wrapBytes = _util.u8aWrapBytes;
23
+ exports.wrapBytes = wrapBytes;
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "author": "Jaco Greeff <jacogr@gmail.com>",
3
+ "bugs": "https://github.com/Koniverse/Subwallet-V2/issues",
4
+ "contributors": [],
5
+ "description": "Provides an interfaces around the injected globals for ease of access by dapp developers.",
6
+ "homepage": "https://github.com/Koniverse/Subwallet-V2/tree/master/packages/extension-dapp#readme",
7
+ "license": "Apache-2.0",
8
+ "maintainers": [],
9
+ "name": "@subwallet/extension-dapp",
10
+ "repository": {
11
+ "directory": "packages/extension-dapp",
12
+ "type": "git",
13
+ "url": "https://github.com/Koniverse/Subwallet-V2.git"
14
+ },
15
+ "sideEffects": [
16
+ "./detectPackage.js",
17
+ "./detectPackage.cjs"
18
+ ],
19
+ "type": "module",
20
+ "version": "0.3.6-0",
21
+ "main": "index.js",
22
+ "dependencies": {
23
+ "@babel/runtime": "^7.16.7",
24
+ "@polkadot/util": "^8.3.1",
25
+ "@polkadot/util-crypto": "^8.3.1",
26
+ "@subwallet/extension-inject": "^0.3.6-0"
27
+ },
28
+ "peerDependencies": {
29
+ "@polkadot/api": "*",
30
+ "@polkadot/util": "*",
31
+ "@polkadot/util-crypto": "*"
32
+ }
33
+ }