@haneullabs/utils 0.1.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 (63) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/b58.d.ts +2 -0
  4. package/dist/cjs/b58.js +28 -0
  5. package/dist/cjs/b58.js.map +7 -0
  6. package/dist/cjs/b64.d.ts +2 -0
  7. package/dist/cjs/b64.js +40 -0
  8. package/dist/cjs/b64.js.map +7 -0
  9. package/dist/cjs/chunk.d.ts +1 -0
  10. package/dist/cjs/chunk.js +29 -0
  11. package/dist/cjs/chunk.js.map +7 -0
  12. package/dist/cjs/dataloader.d.ts +129 -0
  13. package/dist/cjs/dataloader.js +318 -0
  14. package/dist/cjs/dataloader.js.map +7 -0
  15. package/dist/cjs/hex.d.ts +2 -0
  16. package/dist/cjs/hex.js +37 -0
  17. package/dist/cjs/hex.js.map +7 -0
  18. package/dist/cjs/index.d.ts +7 -0
  19. package/dist/cjs/index.js +38 -0
  20. package/dist/cjs/index.js.map +7 -0
  21. package/dist/cjs/package.json +5 -0
  22. package/dist/cjs/types.d.ts +4 -0
  23. package/dist/cjs/types.js +17 -0
  24. package/dist/cjs/types.js.map +7 -0
  25. package/dist/cjs/with-resolver.d.ts +6 -0
  26. package/dist/cjs/with-resolver.js +37 -0
  27. package/dist/cjs/with-resolver.js.map +7 -0
  28. package/dist/esm/b58.d.ts +2 -0
  29. package/dist/esm/b58.js +8 -0
  30. package/dist/esm/b58.js.map +7 -0
  31. package/dist/esm/b64.d.ts +2 -0
  32. package/dist/esm/b64.js +20 -0
  33. package/dist/esm/b64.js.map +7 -0
  34. package/dist/esm/chunk.d.ts +1 -0
  35. package/dist/esm/chunk.js +9 -0
  36. package/dist/esm/chunk.js.map +7 -0
  37. package/dist/esm/dataloader.d.ts +129 -0
  38. package/dist/esm/dataloader.js +298 -0
  39. package/dist/esm/dataloader.js.map +7 -0
  40. package/dist/esm/hex.d.ts +2 -0
  41. package/dist/esm/hex.js +17 -0
  42. package/dist/esm/hex.js.map +7 -0
  43. package/dist/esm/index.d.ts +7 -0
  44. package/dist/esm/index.js +18 -0
  45. package/dist/esm/index.js.map +7 -0
  46. package/dist/esm/package.json +5 -0
  47. package/dist/esm/types.d.ts +4 -0
  48. package/dist/esm/types.js +1 -0
  49. package/dist/esm/types.js.map +7 -0
  50. package/dist/esm/with-resolver.d.ts +6 -0
  51. package/dist/esm/with-resolver.js +17 -0
  52. package/dist/esm/with-resolver.js.map +7 -0
  53. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  54. package/dist/tsconfig.tsbuildinfo +1 -0
  55. package/package.json +53 -0
  56. package/src/b58.ts +7 -0
  57. package/src/b64.ts +22 -0
  58. package/src/chunk.ts +8 -0
  59. package/src/dataloader.ts +518 -0
  60. package/src/hex.ts +18 -0
  61. package/src/index.ts +10 -0
  62. package/src/types.ts +13 -0
  63. package/src/with-resolver.ts +24 -0
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@haneullabs/utils",
3
+ "version": "0.1.0",
4
+ "description": "Shared utilities for @haneullabs/* packages",
5
+ "license": "Apache-2.0",
6
+ "author": "Haneul Labs <build@haneullabs.com>",
7
+ "sideEffects": false,
8
+ "type": "commonjs",
9
+ "main": "./dist/cjs/index.js",
10
+ "module": "./dist/esm/index.js",
11
+ "types": "./dist/cjs/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/esm/index.js",
15
+ "require": "./dist/cjs/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "CHANGELOG.md",
20
+ "dist",
21
+ "src"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/GeunhwaJeong/haneul-ts-sdks.git"
26
+ },
27
+ "keywords": [
28
+ "utils"
29
+ ],
30
+ "bugs": {
31
+ "url": "https://github.com/GeunhwaJeong/haneul-ts-sdks/issues"
32
+ },
33
+ "homepage": "https://github.com/GeunhwaJeong/haneul-ts-sdks/tree/main/packages/utils#readme",
34
+ "devDependencies": {
35
+ "typescript": "^5.9.3",
36
+ "vitest": "^4.0.15",
37
+ "@haneullabs/build-scripts": "0.1.0"
38
+ },
39
+ "dependencies": {
40
+ "@scure/base": "^1.2.6"
41
+ },
42
+ "scripts": {
43
+ "clean": "rm -rf tsconfig.tsbuildinfo ./dist",
44
+ "dev": "pnpm build --watch",
45
+ "build": "build-package",
46
+ "prettier:check": "prettier -c --ignore-unknown .",
47
+ "prettier:fix": "prettier -w --ignore-unknown .",
48
+ "eslint:check": "eslint --max-warnings=0 .",
49
+ "eslint:fix": "pnpm run eslint:check --fix",
50
+ "lint": "pnpm run eslint:check && pnpm run prettier:check",
51
+ "lint:fix": "pnpm run eslint:fix && pnpm run prettier:fix"
52
+ }
53
+ }
package/src/b58.ts ADDED
@@ -0,0 +1,7 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { base58 } from '@scure/base';
5
+
6
+ export const toBase58 = (buffer: Uint8Array) => base58.encode(buffer);
7
+ export const fromBase58 = (str: string) => base58.decode(str) as Uint8Array<ArrayBuffer>;
package/src/b64.ts ADDED
@@ -0,0 +1,22 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export function fromBase64(base64String: string): Uint8Array<ArrayBuffer> {
5
+ return Uint8Array.from(atob(base64String), (char) => char.charCodeAt(0));
6
+ }
7
+
8
+ const CHUNK_SIZE = 8192;
9
+ export function toBase64(bytes: Uint8Array): string {
10
+ // Special-case the simple case for speed's sake.
11
+ if (bytes.length < CHUNK_SIZE) {
12
+ return btoa(String.fromCharCode(...bytes));
13
+ }
14
+
15
+ let output = '';
16
+ for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
17
+ const chunk = bytes.slice(i, i + CHUNK_SIZE);
18
+ output += String.fromCharCode(...chunk);
19
+ }
20
+
21
+ return btoa(output);
22
+ }
package/src/chunk.ts ADDED
@@ -0,0 +1,8 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export function chunk<T>(array: readonly T[], size: number): T[][] {
5
+ return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => {
6
+ return array.slice(i * size, (i + 1) * size);
7
+ });
8
+ }
@@ -0,0 +1,518 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /** Copied from https://github.com/graphql/dataloader/blob/a10773043d41a56bde4219c155fcf5633e6c9bcb/src/index.js */
5
+
6
+ /**
7
+ * A `DataLoader` creates a public API for loading data from a particular
8
+ * data back-end with unique keys such as the `id` column of a SQL table or
9
+ * document name in a MongoDB database, given a batch loading function.
10
+ *
11
+ * Each `DataLoader` instance contains a unique memoized cache. Use caution when
12
+ * used in long-lived applications or those which serve many users with
13
+ * different access permissions and consider creating a new instance per
14
+ * web request.
15
+ */
16
+ export class DataLoader<K, V, C = K> {
17
+ constructor(batchLoadFn: DataLoader.BatchLoadFn<K, V>, options?: DataLoader.Options<K, V, C>) {
18
+ if (typeof batchLoadFn !== 'function') {
19
+ throw new TypeError(
20
+ 'DataLoader must be constructed with a function which accepts ' +
21
+ `Array<key> and returns Promise<Array<value>>, but got: ${batchLoadFn}.`,
22
+ );
23
+ }
24
+ this._batchLoadFn = batchLoadFn;
25
+ this._maxBatchSize = getValidMaxBatchSize(options);
26
+ this._batchScheduleFn = getValidBatchScheduleFn(options);
27
+ this._cacheKeyFn = getValidCacheKeyFn(options);
28
+ this._cacheMap = getValidCacheMap(options);
29
+ this._batch = null;
30
+ this.name = getValidName(options);
31
+ }
32
+
33
+ // Private
34
+ _batchLoadFn: DataLoader.BatchLoadFn<K, V>;
35
+ _maxBatchSize: number;
36
+ _batchScheduleFn: (cb: () => void) => void;
37
+ _cacheKeyFn: (key: K) => C;
38
+ _cacheMap: DataLoader.CacheMap<C, Promise<V>> | null;
39
+ _batch: Batch<K, V> | null;
40
+
41
+ /**
42
+ * Loads a key, returning a `Promise` for the value represented by that key.
43
+ */
44
+ load(key: K): Promise<V> {
45
+ if (key === null || key === undefined) {
46
+ throw new TypeError(
47
+ `The loader.load() function must be called with a value, but got: ${String(key)}.`,
48
+ );
49
+ }
50
+
51
+ const batch = getCurrentBatch(this);
52
+ const cacheMap = this._cacheMap;
53
+ let cacheKey: C;
54
+
55
+ // If caching and there is a cache-hit, return cached Promise.
56
+ if (cacheMap) {
57
+ cacheKey = this._cacheKeyFn(key);
58
+ const cachedPromise = cacheMap.get(cacheKey);
59
+ if (cachedPromise) {
60
+ const cacheHits = batch.cacheHits || (batch.cacheHits = []);
61
+ return new Promise((resolve) => {
62
+ cacheHits.push(() => {
63
+ resolve(cachedPromise);
64
+ });
65
+ });
66
+ }
67
+ }
68
+
69
+ // Otherwise, produce a new Promise for this key, and enqueue it to be
70
+ // dispatched along with the current batch.
71
+ batch.keys.push(key);
72
+ const promise = new Promise<V>((resolve, reject) => {
73
+ batch.callbacks.push({ resolve, reject });
74
+ });
75
+
76
+ // If caching, cache this promise.
77
+ if (cacheMap) {
78
+ cacheMap.set(cacheKey!, promise);
79
+ }
80
+
81
+ return promise;
82
+ }
83
+
84
+ /**
85
+ * Loads multiple keys, promising an array of values:
86
+ *
87
+ * var [ a, b ] = await myLoader.loadMany([ 'a', 'b' ]);
88
+ *
89
+ * This is similar to the more verbose:
90
+ *
91
+ * var [ a, b ] = await Promise.all([
92
+ * myLoader.load('a'),
93
+ * myLoader.load('b')
94
+ * ]);
95
+ *
96
+ * However it is different in the case where any load fails. Where
97
+ * Promise.all() would reject, loadMany() always resolves, however each result
98
+ * is either a value or an Error instance.
99
+ *
100
+ * var [ a, b, c ] = await myLoader.loadMany([ 'a', 'b', 'badkey' ]);
101
+ * // c instanceof Error
102
+ *
103
+ */
104
+ loadMany(keys: ReadonlyArray<K>): Promise<Array<V | Error>> {
105
+ if (!isArrayLike(keys)) {
106
+ throw new TypeError(
107
+ `The loader.loadMany() function must be called with Array<key>, but got: ${keys}.`,
108
+ );
109
+ }
110
+ // Support ArrayLike by using only minimal property access
111
+ const loadPromises = [];
112
+ for (let i = 0; i < keys.length; i++) {
113
+ loadPromises.push(this.load(keys[i]).catch((error) => error));
114
+ }
115
+ return Promise.all(loadPromises);
116
+ }
117
+
118
+ /**
119
+ * Clears the value at `key` from the cache, if it exists. Returns itself for
120
+ * method chaining.
121
+ */
122
+ clear(key: K): this {
123
+ const cacheMap = this._cacheMap;
124
+ if (cacheMap) {
125
+ const cacheKey = this._cacheKeyFn(key);
126
+ cacheMap.delete(cacheKey);
127
+ }
128
+ return this;
129
+ }
130
+
131
+ /**
132
+ * Clears the entire cache. To be used when some event results in unknown
133
+ * invalidations across this particular `DataLoader`. Returns itself for
134
+ * method chaining.
135
+ */
136
+ clearAll(): this {
137
+ const cacheMap = this._cacheMap;
138
+ if (cacheMap) {
139
+ cacheMap.clear();
140
+ }
141
+ return this;
142
+ }
143
+
144
+ /**
145
+ * Adds the provided key and value to the cache. If the key already
146
+ * exists, no change is made. Returns itself for method chaining.
147
+ *
148
+ * To prime the cache with an error at a key, provide an Error instance.
149
+ */
150
+ prime(key: K, value: V | Promise<V> | Error): this {
151
+ const cacheMap = this._cacheMap;
152
+ if (cacheMap) {
153
+ const cacheKey = this._cacheKeyFn(key);
154
+
155
+ // Only add the key if it does not already exist.
156
+ if (cacheMap.get(cacheKey) === undefined) {
157
+ // Cache a rejected promise if the value is an Error, in order to match
158
+ // the behavior of load(key).
159
+ let promise;
160
+ if (value instanceof Error) {
161
+ promise = Promise.reject(value);
162
+ // Since this is a case where an Error is intentionally being primed
163
+ // for a given key, we want to disable unhandled promise rejection.
164
+ promise.catch(() => {});
165
+ } else {
166
+ promise = Promise.resolve(value);
167
+ }
168
+ cacheMap.set(cacheKey, promise);
169
+ }
170
+ }
171
+ return this;
172
+ }
173
+
174
+ /**
175
+ * The name given to this `DataLoader` instance. Useful for APM tools.
176
+ *
177
+ * Is `null` if not set in the constructor.
178
+ */
179
+ name: string | null;
180
+ }
181
+
182
+ // Private: Enqueue a Job to be executed after all "PromiseJobs" Jobs.
183
+ //
184
+ // ES6 JavaScript uses the concepts Job and JobQueue to schedule work to occur
185
+ // after the current execution context has completed:
186
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-jobs-and-job-queues
187
+ //
188
+ // Node.js uses the `process.nextTick` mechanism to implement the concept of a
189
+ // Job, maintaining a global FIFO JobQueue for all Jobs, which is flushed after
190
+ // the current call stack ends.
191
+ //
192
+ // When calling `then` on a Promise, it enqueues a Job on a specific
193
+ // "PromiseJobs" JobQueue which is flushed in Node as a single Job on the
194
+ // global JobQueue.
195
+ //
196
+ // DataLoader batches all loads which occur in a single frame of execution, but
197
+ // should include in the batch all loads which occur during the flushing of the
198
+ // "PromiseJobs" JobQueue after that same execution frame.
199
+ //
200
+ // In order to avoid the DataLoader dispatch Job occuring before "PromiseJobs",
201
+ // A Promise Job is created with the sole purpose of enqueuing a global Job,
202
+ // ensuring that it always occurs after "PromiseJobs" ends.
203
+ //
204
+ // Node.js's job queue is unique. Browsers do not have an equivalent mechanism
205
+ // for enqueuing a job to be performed after promise microtasks and before the
206
+ // next macrotask. For browser environments, a macrotask is used (via
207
+ // setImmediate or setTimeout) at a potential performance penalty.
208
+ const enqueuePostPromiseJob: (fn: () => void) => void =
209
+ // @ts-ignore
210
+ typeof process === 'object' && typeof process.nextTick === 'function'
211
+ ? function (fn) {
212
+ if (!resolvedPromise) {
213
+ resolvedPromise = Promise.resolve();
214
+ }
215
+ resolvedPromise.then(() => {
216
+ // @ts-ignore
217
+ process.nextTick(fn);
218
+ });
219
+ }
220
+ : // @ts-ignore
221
+ typeof setImmediate === 'function'
222
+ ? function (fn) {
223
+ // @ts-ignore
224
+ setImmediate(fn);
225
+ }
226
+ : function (fn) {
227
+ setTimeout(fn);
228
+ };
229
+
230
+ // Private: cached resolved Promise instance
231
+ let resolvedPromise: Promise<void> | undefined;
232
+
233
+ // Private: Describes a batch of requests
234
+ type Batch<K, V> = {
235
+ hasDispatched: boolean;
236
+ keys: Array<K>;
237
+ callbacks: Array<{
238
+ resolve: (value: V) => void;
239
+ reject: (error: Error) => void;
240
+ }>;
241
+ cacheHits?: Array<() => void>;
242
+ };
243
+
244
+ // Private: Either returns the current batch, or creates and schedules a
245
+ // dispatch of a new batch for the given loader.
246
+ function getCurrentBatch<K, V>(loader: DataLoader<K, V, any>): Batch<K, V> {
247
+ // If there is an existing batch which has not yet dispatched and is within
248
+ // the limit of the batch size, then return it.
249
+ const existingBatch = loader._batch;
250
+ if (
251
+ existingBatch !== null &&
252
+ !existingBatch.hasDispatched &&
253
+ existingBatch.keys.length < loader._maxBatchSize
254
+ ) {
255
+ return existingBatch;
256
+ }
257
+
258
+ // Otherwise, create a new batch for this loader.
259
+ const newBatch = { hasDispatched: false, keys: [], callbacks: [] };
260
+
261
+ // Store it on the loader so it may be reused.
262
+ loader._batch = newBatch;
263
+
264
+ // Then schedule a task to dispatch this batch of requests.
265
+ loader._batchScheduleFn(() => {
266
+ dispatchBatch(loader, newBatch);
267
+ });
268
+
269
+ return newBatch;
270
+ }
271
+
272
+ function dispatchBatch<K, V>(loader: DataLoader<K, V, any>, batch: Batch<K, V>) {
273
+ // Mark this batch as having been dispatched.
274
+ batch.hasDispatched = true;
275
+
276
+ // If there's nothing to load, resolve any cache hits and return early.
277
+ if (batch.keys.length === 0) {
278
+ resolveCacheHits(batch);
279
+ return;
280
+ }
281
+
282
+ // Call the provided batchLoadFn for this loader with the batch's keys and
283
+ // with the loader as the `this` context.
284
+ let batchPromise;
285
+ try {
286
+ batchPromise = loader._batchLoadFn(batch.keys);
287
+ } catch (e) {
288
+ return failedDispatch(
289
+ loader,
290
+ batch,
291
+ new TypeError(
292
+ 'DataLoader must be constructed with a function which accepts ' +
293
+ 'Array<key> and returns Promise<Array<value>>, but the function ' +
294
+ `errored synchronously: ${String(e)}.`,
295
+ ),
296
+ );
297
+ }
298
+
299
+ // Assert the expected response from batchLoadFn
300
+ if (!batchPromise || typeof batchPromise.then !== 'function') {
301
+ return failedDispatch(
302
+ loader,
303
+ batch,
304
+ new TypeError(
305
+ 'DataLoader must be constructed with a function which accepts ' +
306
+ 'Array<key> and returns Promise<Array<value>>, but the function did ' +
307
+ `not return a Promise: ${String(batchPromise)}.`,
308
+ ),
309
+ );
310
+ }
311
+
312
+ // Await the resolution of the call to batchLoadFn.
313
+ Promise.resolve(batchPromise)
314
+ .then((values) => {
315
+ // Assert the expected resolution from batchLoadFn.
316
+ if (!isArrayLike(values)) {
317
+ throw new TypeError(
318
+ 'DataLoader must be constructed with a function which accepts ' +
319
+ 'Array<key> and returns Promise<Array<value>>, but the function did ' +
320
+ `not return a Promise of an Array: ${String(values)}.`,
321
+ );
322
+ }
323
+ if (values.length !== batch.keys.length) {
324
+ throw new TypeError(
325
+ 'DataLoader must be constructed with a function which accepts ' +
326
+ 'Array<key> and returns Promise<Array<value>>, but the function did ' +
327
+ 'not return a Promise of an Array of the same length as the Array ' +
328
+ 'of keys.' +
329
+ `\n\nKeys:\n${String(batch.keys)}` +
330
+ `\n\nValues:\n${String(values)}`,
331
+ );
332
+ }
333
+
334
+ // Resolve all cache hits in the same micro-task as freshly loaded values.
335
+ resolveCacheHits(batch);
336
+
337
+ // Step through values, resolving or rejecting each Promise in the batch.
338
+ for (let i = 0; i < batch.callbacks.length; i++) {
339
+ const value = values[i];
340
+ if (value instanceof Error) {
341
+ batch.callbacks[i].reject(value);
342
+ } else {
343
+ batch.callbacks[i].resolve(value);
344
+ }
345
+ }
346
+ })
347
+ .catch((error: unknown) => {
348
+ failedDispatch(loader, batch, error as Error);
349
+ });
350
+ }
351
+
352
+ // Private: do not cache individual loads if the entire batch dispatch fails,
353
+ // but still reject each request so they do not hang.
354
+ function failedDispatch<K, V>(loader: DataLoader<K, V, any>, batch: Batch<K, V>, error: Error) {
355
+ // Cache hits are resolved, even though the batch failed.
356
+ resolveCacheHits(batch);
357
+ for (let i = 0; i < batch.keys.length; i++) {
358
+ loader.clear(batch.keys[i]);
359
+ batch.callbacks[i].reject(error);
360
+ }
361
+ }
362
+
363
+ // Private: Resolves the Promises for any cache hits in this batch.
364
+ function resolveCacheHits(batch: Batch<any, any>) {
365
+ if (batch.cacheHits) {
366
+ for (let i = 0; i < batch.cacheHits.length; i++) {
367
+ batch.cacheHits[i]();
368
+ }
369
+ }
370
+ }
371
+
372
+ // Private: given the DataLoader's options, produce a valid max batch size.
373
+ function getValidMaxBatchSize<K, V, C>(options?: DataLoader.Options<K, V, C>): number {
374
+ const shouldBatch = !options || options.batch !== false;
375
+ if (!shouldBatch) {
376
+ return 1;
377
+ }
378
+ const maxBatchSize = options && options.maxBatchSize;
379
+ if (maxBatchSize === undefined) {
380
+ return Infinity;
381
+ }
382
+ if (typeof maxBatchSize !== 'number' || maxBatchSize < 1) {
383
+ throw new TypeError(`maxBatchSize must be a positive number: ${maxBatchSize}`);
384
+ }
385
+ return maxBatchSize;
386
+ }
387
+
388
+ // Private
389
+ function getValidBatchScheduleFn<K, V, C>(
390
+ options?: DataLoader.Options<K, V, C>,
391
+ ): (cb: () => void) => void {
392
+ const batchScheduleFn = options && options.batchScheduleFn;
393
+ if (batchScheduleFn === undefined) {
394
+ return enqueuePostPromiseJob;
395
+ }
396
+ if (typeof batchScheduleFn !== 'function') {
397
+ throw new TypeError(`batchScheduleFn must be a function: ${batchScheduleFn}`);
398
+ }
399
+ return batchScheduleFn;
400
+ }
401
+
402
+ // Private: given the DataLoader's options, produce a cache key function.
403
+ function getValidCacheKeyFn<K, V, C>(options?: DataLoader.Options<K, V, C>): (key: K) => C {
404
+ const cacheKeyFn = options && options.cacheKeyFn;
405
+ if (cacheKeyFn === undefined) {
406
+ return (key: K) => key as unknown as C;
407
+ }
408
+ if (typeof cacheKeyFn !== 'function') {
409
+ throw new TypeError(`cacheKeyFn must be a function: ${cacheKeyFn}`);
410
+ }
411
+ return cacheKeyFn;
412
+ }
413
+
414
+ // Private: given the DataLoader's options, produce a CacheMap to be used.
415
+ function getValidCacheMap<K, V, C>(
416
+ options?: DataLoader.Options<K, V, C>,
417
+ ): DataLoader.CacheMap<C, Promise<V>> | null {
418
+ const shouldCache = !options || options.cache !== false;
419
+ if (!shouldCache) {
420
+ return null;
421
+ }
422
+ const cacheMap = options && options.cacheMap;
423
+ if (cacheMap === undefined) {
424
+ return new Map();
425
+ }
426
+ if (cacheMap !== null) {
427
+ const cacheFunctions = ['get', 'set', 'delete', 'clear'] as const;
428
+ const missingFunctions = cacheFunctions.filter(
429
+ (fnName) => cacheMap && typeof cacheMap[fnName] !== 'function',
430
+ );
431
+ if (missingFunctions.length !== 0) {
432
+ throw new TypeError('Custom cacheMap missing methods: ' + missingFunctions.join(', '));
433
+ }
434
+ }
435
+ return cacheMap;
436
+ }
437
+
438
+ function getValidName<K, V, C>(options?: DataLoader.Options<K, V, C>): string | null {
439
+ if (options && options.name) {
440
+ return options.name;
441
+ }
442
+
443
+ return null;
444
+ }
445
+
446
+ function isArrayLike(x: unknown): x is ArrayLike<unknown> {
447
+ return (
448
+ typeof x === 'object' &&
449
+ x !== null &&
450
+ 'length' in x &&
451
+ typeof x.length === 'number' &&
452
+ (x.length === 0 || (x.length > 0 && Object.prototype.hasOwnProperty.call(x, x.length - 1)))
453
+ );
454
+ }
455
+
456
+ export declare namespace DataLoader {
457
+ // If a custom cache is provided, it must be of this type (a subset of ES6 Map).
458
+ export type CacheMap<K, V> = {
459
+ get(key: K): V | void;
460
+ set(key: K, value: V): any;
461
+ delete(key: K): any;
462
+ clear(): any;
463
+ };
464
+
465
+ // A Function, which when given an Array of keys, returns a Promise of an Array
466
+ // of values or Errors.
467
+ export type BatchLoadFn<K, V> = (keys: ReadonlyArray<K>) => PromiseLike<ArrayLike<V | Error>>;
468
+
469
+ // Optionally turn off batching or caching or provide a cache key function or a
470
+ // custom cache instance.
471
+ export type Options<K, V, C = K> = {
472
+ /**
473
+ * Default `true`. Set to `false` to disable batching, invoking
474
+ * `batchLoadFn` with a single load key. This is equivalent to setting
475
+ * `maxBatchSize` to `1`.
476
+ */
477
+ batch?: boolean;
478
+
479
+ /**
480
+ * Default `Infinity`. Limits the number of items that get passed in to the
481
+ * `batchLoadFn`. May be set to `1` to disable batching.
482
+ */
483
+ maxBatchSize?: number;
484
+
485
+ /**
486
+ * Default see https://github.com/graphql/dataloader#batch-scheduling.
487
+ * A function to schedule the later execution of a batch. The function is
488
+ * expected to call the provided callback in the immediate future.
489
+ */
490
+ batchScheduleFn?: (callback: () => void) => void;
491
+
492
+ /**
493
+ * Default `true`. Set to `false` to disable memoization caching, creating a
494
+ * new Promise and new key in the `batchLoadFn` for every load of the same
495
+ * key. This is equivalent to setting `cacheMap` to `null`.
496
+ */
497
+ cache?: boolean;
498
+
499
+ /**
500
+ * Default `key => key`. Produces cache key for a given load key. Useful
501
+ * when keys are objects and two objects should be considered equivalent.
502
+ */
503
+ cacheKeyFn?: (key: K) => C;
504
+
505
+ /**
506
+ * Default `new Map()`. Instance of `Map` (or an object with a similar API)
507
+ * to be used as cache. May be set to `null` to disable caching.
508
+ */
509
+ cacheMap?: CacheMap<C, Promise<V>> | null;
510
+
511
+ /**
512
+ * The name given to this `DataLoader` instance. Useful for APM tools.
513
+ *
514
+ * Is `null` if not set in the constructor.
515
+ */
516
+ name?: string | null;
517
+ };
518
+ }
package/src/hex.ts ADDED
@@ -0,0 +1,18 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export function fromHex(hexStr: string): Uint8Array<ArrayBuffer> {
5
+ const normalized = hexStr.startsWith('0x') ? hexStr.slice(2) : hexStr;
6
+ const padded = normalized.length % 2 === 0 ? normalized : `0${normalized}`;
7
+ const intArr = padded.match(/[0-9a-fA-F]{2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
8
+
9
+ if (intArr.length !== padded.length / 2) {
10
+ throw new Error(`Invalid hex string ${hexStr}`);
11
+ }
12
+
13
+ return Uint8Array.from(intArr);
14
+ }
15
+
16
+ export function toHex(bytes: Uint8Array): string {
17
+ return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { fromBase58, toBase58 } from './b58.js';
5
+ export { fromBase64, toBase64 } from './b64.js';
6
+ export { fromHex, toHex } from './hex.js';
7
+ export type { Simplify, UnionToIntersection } from './types.js';
8
+ export { chunk } from './chunk.js';
9
+ export { promiseWithResolvers, type PromiseWithResolvers } from './with-resolver.js';
10
+ export { DataLoader } from './dataloader.js';
package/src/types.ts ADDED
@@ -0,0 +1,13 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export type Simplify<T> = {
5
+ [K in keyof T]: T[K];
6
+ // eslint-disable-next-line @typescript-eslint/ban-types
7
+ } & {};
8
+
9
+ export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
10
+ k: infer I,
11
+ ) => void
12
+ ? I
13
+ : never;
@@ -0,0 +1,24 @@
1
+ // Copyright (c) Mysten Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export interface PromiseWithResolvers<T> {
5
+ promise: Promise<T>;
6
+ resolve: (value: T) => void;
7
+ reject: (error: unknown) => void;
8
+ }
9
+
10
+ export function promiseWithResolvers<T>(): PromiseWithResolvers<T> {
11
+ let resolver!: (value: T) => void;
12
+ let rejecter!: (error: unknown) => void;
13
+
14
+ const promise = new Promise<T>((resolve, reject) => {
15
+ resolver = resolve;
16
+ rejecter = reject;
17
+ });
18
+
19
+ return {
20
+ promise,
21
+ resolve: resolver,
22
+ reject: rejecter,
23
+ };
24
+ }