@gibs/common 1.13.0 → 1.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,110 @@
1
+ /**
2
+ * Request addresses, built and read with plain string work.
3
+ *
4
+ * WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
5
+ * `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
6
+ * `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
7
+ * implemented". `searchParams` is always a new, empty list, so a reader of it
8
+ * silently sees no parameters. `URLSearchParams` can only append and
9
+ * serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
10
+ * its constructor is ignored. A package that uses either class needs a
11
+ * polyfill in a wallet. These functions need nothing from the runtime but
12
+ * `encodeURIComponent` and `decodeURIComponent`.
13
+ *
14
+ * Their output matches what the WHATWG classes produce for the addresses the
15
+ * Gibs packages build: a lowercase `http:` or `https:` origin with a plain
16
+ * path. A query is encoded exactly as `URLSearchParams` encodes it, as
17
+ * `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
18
+ * digits and `*-._` stay as they are. They do not normalize the address
19
+ * itself: an uppercase host or a `..` path segment passes through as written.
20
+ */
21
+ /**
22
+ * Query parameters, as a record or as ordered name and value pairs.
23
+ *
24
+ * In a record, a value of `undefined` leaves that parameter out, so an
25
+ * optional parameter can be written in place. Pairs keep their order and may
26
+ * repeat a name.
27
+ */
28
+ export type QueryParameters = Readonly<Record<string, string | undefined>> | ReadonlyArray<readonly [string, string]>;
29
+ /** One query parameter: its decoded name and value. */
30
+ export type QueryEntry = readonly [name: string, value: string];
31
+ /** The parts of an address, as the WHATWG `URL` getters name them. */
32
+ export type UrlParts = {
33
+ /** The scheme in lowercase, with no colon, such as `https`. Empty when the address has none. */
34
+ readonly scheme: string;
35
+ /** The host in lowercase, with the port unless it is the scheme's default. Empty when the address has no authority. */
36
+ readonly host: string;
37
+ /** The path. `/` for an address with an authority and no path. */
38
+ readonly path: string;
39
+ /** The query, with no leading `?`. */
40
+ readonly query: string;
41
+ /** The fragment, with no leading `#`. */
42
+ readonly fragment: string;
43
+ };
44
+ /**
45
+ * Encodes a query name or value exactly as `URLSearchParams.toString` does.
46
+ *
47
+ * @param text - the raw name or value
48
+ * @returns the encoded text
49
+ */
50
+ export declare const encodeQueryComponent: (text: string) => string;
51
+ /**
52
+ * Decodes a query name or value as the WHATWG URL standard says
53
+ * `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
54
+ * starts no valid sequence stays as it is, and each malformed UTF-8 part, or
55
+ * lone surrogate half, becomes U+FFFD.
56
+ *
57
+ * @param text - the encoded name or value
58
+ * @returns the decoded text
59
+ */
60
+ export declare const decodeQueryComponent: (text: string) => string;
61
+ /**
62
+ * Lists the parameters of a query, decoded and in order.
63
+ *
64
+ * @param query - a query, with or without its leading `?`
65
+ * @returns each parameter's name and value; a parameter with no `=` has an empty value
66
+ */
67
+ export declare const parseQuery: (query: string) => readonly QueryEntry[];
68
+ /**
69
+ * Writes a query, as `new URLSearchParams(parameters).toString()` does.
70
+ *
71
+ * @param parameters - a record or pairs; see {@link QueryParameters}
72
+ * @returns the encoded query, with no leading `?`; empty for no parameters
73
+ */
74
+ export declare const buildQuery: (parameters: QueryParameters) => string;
75
+ /**
76
+ * Sets one parameter in a query, as `URLSearchParams.set` does: the first
77
+ * parameter of that name takes the value and any later ones are removed, or
78
+ * the parameter is appended when none has the name.
79
+ *
80
+ * @param query - an encoded query, with or without its leading `?`
81
+ * @param name - the parameter's name
82
+ * @param value - its value
83
+ * @returns the encoded query, with no leading `?`
84
+ */
85
+ export declare const setQueryParameter: (query: string, name: string, value: string) => string;
86
+ /**
87
+ * Adds query parameters to an address, as `url.searchParams.set` does for
88
+ * each one in turn. A query already on the address is kept, and a fragment
89
+ * stays at the end.
90
+ *
91
+ * @param address - the address, with or without a query
92
+ * @param parameters - a record or pairs; see {@link QueryParameters}
93
+ * @returns the address with the parameters set; the address unchanged when there are none
94
+ */
95
+ export declare const buildUrl: (address: string, parameters: QueryParameters) => string;
96
+ /**
97
+ * Splits an address into its parts, without the WHATWG `URL` class.
98
+ *
99
+ * @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
100
+ * @returns the parts; see {@link UrlParts}
101
+ */
102
+ export declare const splitUrl: (address: string) => UrlParts;
103
+ /**
104
+ * Says whether a value is an absolute `http:` or `https:` address with a host.
105
+ * It reads the scheme by its prefix, so it needs no `URL` class.
106
+ *
107
+ * @param value - any string
108
+ * @returns true for an absolute `http:` or `https:` address
109
+ */
110
+ export declare const isHttpUrl: (value: string) => boolean;
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isHttpUrl = exports.splitUrl = exports.buildUrl = exports.setQueryParameter = exports.buildQuery = exports.parseQuery = exports.decodeQueryComponent = exports.encodeQueryComponent = void 0;
4
+ /**
5
+ * Request addresses, built and read with plain string work.
6
+ *
7
+ * WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
8
+ * `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
9
+ * `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
10
+ * implemented". `searchParams` is always a new, empty list, so a reader of it
11
+ * silently sees no parameters. `URLSearchParams` can only append and
12
+ * serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
13
+ * its constructor is ignored. A package that uses either class needs a
14
+ * polyfill in a wallet. These functions need nothing from the runtime but
15
+ * `encodeURIComponent` and `decodeURIComponent`.
16
+ *
17
+ * Their output matches what the WHATWG classes produce for the addresses the
18
+ * Gibs packages build: a lowercase `http:` or `https:` origin with a plain
19
+ * path. A query is encoded exactly as `URLSearchParams` encodes it, as
20
+ * `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
21
+ * digits and `*-._` stay as they are. They do not normalize the address
22
+ * itself: an uppercase host or a `..` path segment passes through as written.
23
+ */
24
+ /** A surrogate pair, or a lone surrogate half. */
25
+ const surrogates = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF]/g;
26
+ /** The characters `encodeURIComponent` keeps that a form encoder escapes. */
27
+ const formEscapedCharacters = /[!'()~]/g;
28
+ /** A run of percent-encoded bytes. */
29
+ const percentEncodedRun = /(?:%[0-9A-Fa-f]{2})+/g;
30
+ /** The port each special scheme leaves out of its host. */
31
+ const defaultPorts = Object.freeze({
32
+ http: '80',
33
+ https: '443',
34
+ ws: '80',
35
+ wss: '443',
36
+ ftp: '21',
37
+ });
38
+ /**
39
+ * Replaces each lone surrogate half with U+FFFD, as the WHATWG classes do
40
+ * before they encode. `encodeURIComponent` would throw on one instead.
41
+ *
42
+ * @param text - any string
43
+ * @returns the string, well formed
44
+ */
45
+ const toWellFormed = (text) => text.replace(surrogates, (match) => (match.length === 2 ? match : '�'));
46
+ /**
47
+ * Percent-encodes one character as two uppercase hex digits.
48
+ *
49
+ * @param character - a single ASCII character
50
+ * @returns the character as `%XX`
51
+ */
52
+ const percentEncodeCharacter = (character) => `%${character.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`;
53
+ /**
54
+ * Encodes a query name or value exactly as `URLSearchParams.toString` does.
55
+ *
56
+ * @param text - the raw name or value
57
+ * @returns the encoded text
58
+ */
59
+ const encodeQueryComponent = (text) => encodeURIComponent(toWellFormed(text))
60
+ .replace(formEscapedCharacters, percentEncodeCharacter)
61
+ .replace(/%20/g, '+');
62
+ exports.encodeQueryComponent = encodeQueryComponent;
63
+ /**
64
+ * Counts the continuation bytes a UTF-8 lead byte announces, and the bounds
65
+ * the first of them must fall in. The bounds reject overlong forms and
66
+ * surrogates, as the WHATWG decoder does.
67
+ *
68
+ * @param lead - the first byte of a sequence
69
+ * @returns the count, the lead byte's own bits, and the first continuation byte's bounds; null for a byte that cannot start one
70
+ */
71
+ const utf8Lead = (lead) => {
72
+ if (lead >= 0xc2 && lead <= 0xdf)
73
+ return { count: 1, bits: lead & 0x1f, lower: 0x80, upper: 0xbf };
74
+ if (lead === 0xe0)
75
+ return { count: 2, bits: lead & 0x0f, lower: 0xa0, upper: 0xbf };
76
+ if (lead === 0xed)
77
+ return { count: 2, bits: lead & 0x0f, lower: 0x80, upper: 0x9f };
78
+ if (lead >= 0xe1 && lead <= 0xef)
79
+ return { count: 2, bits: lead & 0x0f, lower: 0x80, upper: 0xbf };
80
+ if (lead === 0xf0)
81
+ return { count: 3, bits: lead & 0x07, lower: 0x90, upper: 0xbf };
82
+ if (lead === 0xf4)
83
+ return { count: 3, bits: lead & 0x07, lower: 0x80, upper: 0x8f };
84
+ if (lead >= 0xf1 && lead <= 0xf3)
85
+ return { count: 3, bits: lead & 0x07, lower: 0x80, upper: 0xbf };
86
+ return null;
87
+ };
88
+ /**
89
+ * Decodes UTF-8 bytes as the WHATWG decoder does: each malformed part becomes
90
+ * one U+FFFD, and a byte that ends a malformed part is read again as the
91
+ * start of the next. Only reached when `decodeURIComponent` refuses a run.
92
+ *
93
+ * @param bytes - the bytes
94
+ * @returns the decoded text
95
+ */
96
+ const decodeUtf8Leniently = (bytes) => {
97
+ const decode = (index, decoded) => {
98
+ if (index >= bytes.length)
99
+ return decoded;
100
+ const lead = bytes[index];
101
+ if (lead < 0x80)
102
+ return decode(index + 1, decoded + String.fromCharCode(lead));
103
+ const shape = utf8Lead(lead);
104
+ if (shape === null)
105
+ return decode(index + 1, `${decoded}�`);
106
+ const readContinuation = (offset, codePoint) => {
107
+ if (offset > shape.count)
108
+ return { codePoint, length: offset };
109
+ const byte = bytes[index + offset];
110
+ const lower = offset === 1 ? shape.lower : 0x80;
111
+ const upper = offset === 1 ? shape.upper : 0xbf;
112
+ if (byte === undefined || byte < lower || byte > upper)
113
+ return { codePoint: -1, length: offset };
114
+ return readContinuation(offset + 1, (codePoint << 6) | (byte & 0x3f));
115
+ };
116
+ const { codePoint, length } = readContinuation(1, shape.bits);
117
+ if (codePoint === -1)
118
+ return decode(index + length, `${decoded}�`);
119
+ return decode(index + length, decoded + String.fromCodePoint(codePoint));
120
+ };
121
+ return decode(0, '');
122
+ };
123
+ /**
124
+ * Decodes one run of `%XX` bytes.
125
+ *
126
+ * @param run - consecutive `%XX` sequences
127
+ * @returns the text they encode
128
+ */
129
+ const decodePercentRun = (run) => {
130
+ try {
131
+ return decodeURIComponent(run);
132
+ }
133
+ catch {
134
+ return decodeUtf8Leniently(run.slice(1).split('%').map((pair) => Number.parseInt(pair, 16)));
135
+ }
136
+ };
137
+ /**
138
+ * Decodes a query name or value as the WHATWG URL standard says
139
+ * `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
140
+ * starts no valid sequence stays as it is, and each malformed UTF-8 part, or
141
+ * lone surrogate half, becomes U+FFFD.
142
+ *
143
+ * @param text - the encoded name or value
144
+ * @returns the decoded text
145
+ */
146
+ const decodeQueryComponent = (text) => toWellFormed(text).replace(/\+/g, ' ').replace(percentEncodedRun, decodePercentRun);
147
+ exports.decodeQueryComponent = decodeQueryComponent;
148
+ /**
149
+ * Lists the parameters of a query, decoded and in order.
150
+ *
151
+ * @param query - a query, with or without its leading `?`
152
+ * @returns each parameter's name and value; a parameter with no `=` has an empty value
153
+ */
154
+ const parseQuery = (query) => query
155
+ .replace(/^\?/, '')
156
+ .split('&')
157
+ .filter((part) => part !== '')
158
+ .map((part) => {
159
+ const equalsAt = part.indexOf('=');
160
+ const name = equalsAt === -1 ? part : part.slice(0, equalsAt);
161
+ const value = equalsAt === -1 ? '' : part.slice(equalsAt + 1);
162
+ return [(0, exports.decodeQueryComponent)(name), (0, exports.decodeQueryComponent)(value)];
163
+ });
164
+ exports.parseQuery = parseQuery;
165
+ /**
166
+ * Turns query parameters into ordered pairs.
167
+ *
168
+ * @param parameters - a record or pairs; see {@link QueryParameters}
169
+ * @returns the pairs, with every `undefined` record value left out
170
+ */
171
+ const toEntries = (parameters) => {
172
+ if (Array.isArray(parameters))
173
+ return parameters;
174
+ return Object.entries(parameters).filter((entry) => entry[1] !== undefined);
175
+ };
176
+ /**
177
+ * Writes a query, as `new URLSearchParams(parameters).toString()` does.
178
+ *
179
+ * @param parameters - a record or pairs; see {@link QueryParameters}
180
+ * @returns the encoded query, with no leading `?`; empty for no parameters
181
+ */
182
+ const buildQuery = (parameters) => toEntries(parameters)
183
+ .map(([name, value]) => `${(0, exports.encodeQueryComponent)(name)}=${(0, exports.encodeQueryComponent)(value)}`)
184
+ .join('&');
185
+ exports.buildQuery = buildQuery;
186
+ /**
187
+ * Sets one parameter in a query, as `URLSearchParams.set` does: the first
188
+ * parameter of that name takes the value and any later ones are removed, or
189
+ * the parameter is appended when none has the name.
190
+ *
191
+ * @param query - an encoded query, with or without its leading `?`
192
+ * @param name - the parameter's name
193
+ * @param value - its value
194
+ * @returns the encoded query, with no leading `?`
195
+ */
196
+ const setQueryParameter = (query, name, value) => {
197
+ const entries = (0, exports.parseQuery)(query);
198
+ const firstAt = entries.findIndex(([entryName]) => entryName === name);
199
+ if (firstAt === -1)
200
+ return (0, exports.buildQuery)([...entries, [name, value]]);
201
+ return (0, exports.buildQuery)(entries.flatMap((entry, index) => {
202
+ if (index === firstAt)
203
+ return [[name, value]];
204
+ return entry[0] === name ? [] : [entry];
205
+ }));
206
+ };
207
+ exports.setQueryParameter = setQueryParameter;
208
+ /**
209
+ * Splits an address at its first `#` and then at its first `?`.
210
+ *
211
+ * @param address - any address
212
+ * @returns everything before the query, the query and the fragment, each without its delimiter
213
+ */
214
+ const splitAtQuery = (address) => {
215
+ const fragmentAt = address.indexOf('#');
216
+ const beforeFragment = fragmentAt === -1 ? address : address.slice(0, fragmentAt);
217
+ const fragment = fragmentAt === -1 ? '' : address.slice(fragmentAt + 1);
218
+ const queryAt = beforeFragment.indexOf('?');
219
+ if (queryAt === -1)
220
+ return { beforeQuery: beforeFragment, query: '', fragment };
221
+ return { beforeQuery: beforeFragment.slice(0, queryAt), query: beforeFragment.slice(queryAt + 1), fragment };
222
+ };
223
+ /**
224
+ * Adds query parameters to an address, as `url.searchParams.set` does for
225
+ * each one in turn. A query already on the address is kept, and a fragment
226
+ * stays at the end.
227
+ *
228
+ * @param address - the address, with or without a query
229
+ * @param parameters - a record or pairs; see {@link QueryParameters}
230
+ * @returns the address with the parameters set; the address unchanged when there are none
231
+ */
232
+ const buildUrl = (address, parameters) => {
233
+ const entries = toEntries(parameters);
234
+ if (entries.length === 0)
235
+ return address;
236
+ const { beforeQuery, query, fragment } = splitAtQuery(address);
237
+ const updated = entries.reduce((current, [name, value]) => (0, exports.setQueryParameter)(current, name, value), query);
238
+ return `${beforeQuery}?${updated}${fragment === '' ? '' : `#${fragment}`}`;
239
+ };
240
+ exports.buildUrl = buildUrl;
241
+ /**
242
+ * Reads the host the WHATWG `URL` reports from an authority: lowercase, with
243
+ * any user name and password dropped, and the port dropped when it is the
244
+ * scheme's default.
245
+ *
246
+ * @param authority - the text between `//` and the path
247
+ * @param scheme - the lowercase scheme
248
+ * @returns the host
249
+ */
250
+ const hostOfAuthority = (authority, scheme) => {
251
+ const hostAndPort = authority.slice(authority.lastIndexOf('@') + 1).toLowerCase();
252
+ const port = /:(\d*)$/.exec(hostAndPort);
253
+ if (port === null)
254
+ return hostAndPort;
255
+ const hostname = hostAndPort.slice(0, port.index);
256
+ if (port[1] === '')
257
+ return hostname;
258
+ const portNumber = String(Number(port[1]));
259
+ return portNumber === defaultPorts[scheme] ? hostname : `${hostname}:${portNumber}`;
260
+ };
261
+ /**
262
+ * Splits an address into its parts, without the WHATWG `URL` class.
263
+ *
264
+ * @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
265
+ * @returns the parts; see {@link UrlParts}
266
+ */
267
+ const splitUrl = (address) => {
268
+ const trimmed = address.replace(/^[\u0000- ]+|[\u0000- ]+$/g, '');
269
+ const { beforeQuery, query, fragment } = splitAtQuery(trimmed);
270
+ const schemeMatch = /^([A-Za-z][A-Za-z0-9+.-]*):/.exec(beforeQuery);
271
+ const scheme = schemeMatch === null ? '' : schemeMatch[1].toLowerCase();
272
+ const rest = schemeMatch === null ? beforeQuery : beforeQuery.slice(schemeMatch[0].length);
273
+ if (!rest.startsWith('//'))
274
+ return { scheme, host: '', path: rest, query, fragment };
275
+ const pathAt = rest.indexOf('/', 2);
276
+ const authority = pathAt === -1 ? rest.slice(2) : rest.slice(2, pathAt);
277
+ const path = pathAt === -1 ? '/' : rest.slice(pathAt);
278
+ return { scheme, host: hostOfAuthority(authority, scheme), path, query, fragment };
279
+ };
280
+ exports.splitUrl = splitUrl;
281
+ /**
282
+ * Says whether a value is an absolute `http:` or `https:` address with a host.
283
+ * It reads the scheme by its prefix, so it needs no `URL` class.
284
+ *
285
+ * @param value - any string
286
+ * @returns true for an absolute `http:` or `https:` address
287
+ */
288
+ const isHttpUrl = (value) => {
289
+ const { scheme, host } = (0, exports.splitUrl)(value);
290
+ return (scheme === 'http' || scheme === 'https') && host !== '';
291
+ };
292
+ exports.isHttpUrl = isHttpUrl;
package/dist/client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createPublicClient, createTransport, http, webSocket, } from 'viem';
2
2
  import { countMethods, createRpcPacer, MAX_SAME_METHOD_CALLS_PER_SECOND, } from './rpcPacer.js';
3
+ import { splitUrl } from './url.js';
3
4
  export { MAX_SAME_METHOD_CALLS_PER_SECOND } from './rpcPacer.js';
4
5
  /**
5
6
  * JSON-RPC request batching config. When enabled, viem coalesces requests made
@@ -65,7 +66,7 @@ const pacer = createRpcPacer({ ceiling: MAX_SAME_METHOD_CALLS_PER_SECOND });
65
66
  const paceRequest = async (request) => {
66
67
  // The HOST, not the full url: one proxy serves every chain from one budget,
67
68
  // so `/evm/1` and `/evm/369` must count against the same tally.
68
- const host = new URL(request.url).host;
69
+ const { host } = splitUrl(request.url);
69
70
  await pacer.reserve(host, countMethods(await request.clone().text().catch(() => null)));
70
71
  };
71
72
  /**
package/dist/erc20.d.ts CHANGED
@@ -7,11 +7,9 @@ export declare const erc20MetadataCalls: {
7
7
  /**
8
8
  * The multicall for the erc20 metadata.
9
9
  *
10
- * The explicit annotation is required rather than stylistic: `@types/lodash-es`
11
- * re-exports its `MemoizedFunction` type from a nested `@types/lodash` that a
12
- * generated declaration file cannot name (TS2742). Spelling out the plain call
13
- * signature also keeps the memoization an implementation detail — no caller
14
- * touches the `.cache` property it would otherwise publish.
10
+ * The annotation spells out the plain call signature, which keeps the
11
+ * memoization an implementation detail: no caller touches the `.cache`
12
+ * property it would otherwise publish.
15
13
  */
16
14
  export declare const multicallErc20: (inputs: {
17
15
  client: PublicClient;
package/dist/erc20.js CHANGED
@@ -1,5 +1,5 @@
1
- import * as _ from 'lodash-es';
2
1
  import { erc20Abi, erc20Abi_bytes32, getAddress, } from 'viem';
2
+ import { memoizeByKey } from './cache.js';
3
3
  import { multicallRead } from './multicall.js';
4
4
  /** The erc20 metadata calls */
5
5
  export const erc20MetadataCalls = [
@@ -10,13 +10,11 @@ export const erc20MetadataCalls = [
10
10
  /**
11
11
  * The multicall for the erc20 metadata.
12
12
  *
13
- * The explicit annotation is required rather than stylistic: `@types/lodash-es`
14
- * re-exports its `MemoizedFunction` type from a nested `@types/lodash` that a
15
- * generated declaration file cannot name (TS2742). Spelling out the plain call
16
- * signature also keeps the memoization an implementation detail — no caller
17
- * touches the `.cache` property it would otherwise publish.
13
+ * The annotation spells out the plain call signature, which keeps the
14
+ * memoization an implementation detail: no caller touches the `.cache`
15
+ * property it would otherwise publish.
18
16
  */
19
- export const multicallErc20 = _.memoize(async ({ client, target, chain }) => {
17
+ export const multicallErc20 = memoizeByKey(async ({ client, target, chain }) => {
20
18
  const options = {
21
19
  chain: chain,
22
20
  client: client,
package/dist/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export * from './erc20.js';
5
5
  export * from './multicall.js';
6
6
  export * from './serialize.js';
7
7
  export * from './types.js';
8
+ export * from './url.js';
package/dist/index.js CHANGED
@@ -5,3 +5,4 @@ export * from './erc20.js';
5
5
  export * from './multicall.js';
6
6
  export * from './serialize.js';
7
7
  export * from './types.js';
8
+ export * from './url.js';
package/dist/multicall.js CHANGED
@@ -1,4 +1,3 @@
1
- import * as _ from 'lodash-es';
2
1
  import { decodeFunctionResult, encodeFunctionData, getContract, multicall3Abi, } from 'viem';
3
2
  import { ensureResult } from './assert.js';
4
3
  /**
@@ -206,7 +205,7 @@ export const multicallRead = async ({ chain, client, abi, calls, target, }) => {
206
205
  chainId: client.chain?.id,
207
206
  target,
208
207
  callCount: calls.length,
209
- functionNames: _.uniq(calls.map((call) => call.functionName)),
208
+ functionNames: [...new Set(calls.map((call) => call.functionName))],
210
209
  });
211
210
  throw err;
212
211
  }
package/dist/url.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Request addresses, built and read with plain string work.
3
+ *
4
+ * WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
5
+ * `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
6
+ * `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
7
+ * implemented". `searchParams` is always a new, empty list, so a reader of it
8
+ * silently sees no parameters. `URLSearchParams` can only append and
9
+ * serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
10
+ * its constructor is ignored. A package that uses either class needs a
11
+ * polyfill in a wallet. These functions need nothing from the runtime but
12
+ * `encodeURIComponent` and `decodeURIComponent`.
13
+ *
14
+ * Their output matches what the WHATWG classes produce for the addresses the
15
+ * Gibs packages build: a lowercase `http:` or `https:` origin with a plain
16
+ * path. A query is encoded exactly as `URLSearchParams` encodes it, as
17
+ * `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
18
+ * digits and `*-._` stay as they are. They do not normalize the address
19
+ * itself: an uppercase host or a `..` path segment passes through as written.
20
+ */
21
+ /**
22
+ * Query parameters, as a record or as ordered name and value pairs.
23
+ *
24
+ * In a record, a value of `undefined` leaves that parameter out, so an
25
+ * optional parameter can be written in place. Pairs keep their order and may
26
+ * repeat a name.
27
+ */
28
+ export type QueryParameters = Readonly<Record<string, string | undefined>> | ReadonlyArray<readonly [string, string]>;
29
+ /** One query parameter: its decoded name and value. */
30
+ export type QueryEntry = readonly [name: string, value: string];
31
+ /** The parts of an address, as the WHATWG `URL` getters name them. */
32
+ export type UrlParts = {
33
+ /** The scheme in lowercase, with no colon, such as `https`. Empty when the address has none. */
34
+ readonly scheme: string;
35
+ /** The host in lowercase, with the port unless it is the scheme's default. Empty when the address has no authority. */
36
+ readonly host: string;
37
+ /** The path. `/` for an address with an authority and no path. */
38
+ readonly path: string;
39
+ /** The query, with no leading `?`. */
40
+ readonly query: string;
41
+ /** The fragment, with no leading `#`. */
42
+ readonly fragment: string;
43
+ };
44
+ /**
45
+ * Encodes a query name or value exactly as `URLSearchParams.toString` does.
46
+ *
47
+ * @param text - the raw name or value
48
+ * @returns the encoded text
49
+ */
50
+ export declare const encodeQueryComponent: (text: string) => string;
51
+ /**
52
+ * Decodes a query name or value as the WHATWG URL standard says
53
+ * `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
54
+ * starts no valid sequence stays as it is, and each malformed UTF-8 part, or
55
+ * lone surrogate half, becomes U+FFFD.
56
+ *
57
+ * @param text - the encoded name or value
58
+ * @returns the decoded text
59
+ */
60
+ export declare const decodeQueryComponent: (text: string) => string;
61
+ /**
62
+ * Lists the parameters of a query, decoded and in order.
63
+ *
64
+ * @param query - a query, with or without its leading `?`
65
+ * @returns each parameter's name and value; a parameter with no `=` has an empty value
66
+ */
67
+ export declare const parseQuery: (query: string) => readonly QueryEntry[];
68
+ /**
69
+ * Writes a query, as `new URLSearchParams(parameters).toString()` does.
70
+ *
71
+ * @param parameters - a record or pairs; see {@link QueryParameters}
72
+ * @returns the encoded query, with no leading `?`; empty for no parameters
73
+ */
74
+ export declare const buildQuery: (parameters: QueryParameters) => string;
75
+ /**
76
+ * Sets one parameter in a query, as `URLSearchParams.set` does: the first
77
+ * parameter of that name takes the value and any later ones are removed, or
78
+ * the parameter is appended when none has the name.
79
+ *
80
+ * @param query - an encoded query, with or without its leading `?`
81
+ * @param name - the parameter's name
82
+ * @param value - its value
83
+ * @returns the encoded query, with no leading `?`
84
+ */
85
+ export declare const setQueryParameter: (query: string, name: string, value: string) => string;
86
+ /**
87
+ * Adds query parameters to an address, as `url.searchParams.set` does for
88
+ * each one in turn. A query already on the address is kept, and a fragment
89
+ * stays at the end.
90
+ *
91
+ * @param address - the address, with or without a query
92
+ * @param parameters - a record or pairs; see {@link QueryParameters}
93
+ * @returns the address with the parameters set; the address unchanged when there are none
94
+ */
95
+ export declare const buildUrl: (address: string, parameters: QueryParameters) => string;
96
+ /**
97
+ * Splits an address into its parts, without the WHATWG `URL` class.
98
+ *
99
+ * @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
100
+ * @returns the parts; see {@link UrlParts}
101
+ */
102
+ export declare const splitUrl: (address: string) => UrlParts;
103
+ /**
104
+ * Says whether a value is an absolute `http:` or `https:` address with a host.
105
+ * It reads the scheme by its prefix, so it needs no `URL` class.
106
+ *
107
+ * @param value - any string
108
+ * @returns true for an absolute `http:` or `https:` address
109
+ */
110
+ export declare const isHttpUrl: (value: string) => boolean;