@atproto/common-web 0.2.3 → 0.2.4-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 +6 -0
- package/LICENSE.txt +1 -1
- package/dist/arrays.d.ts +2 -0
- package/dist/arrays.d.ts.map +1 -0
- package/dist/arrays.js +22 -0
- package/dist/arrays.js.map +1 -0
- package/dist/async.d.ts +7 -1
- package/dist/async.d.ts.map +1 -0
- package/dist/async.js +187 -0
- package/dist/async.js.map +1 -0
- package/dist/check.d.ts +1 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +16 -0
- package/dist/check.js.map +1 -0
- package/dist/did-doc.d.ts +3 -1
- package/dist/did-doc.d.ts.map +1 -0
- package/dist/did-doc.js +138 -0
- package/dist/did-doc.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -14188
- package/dist/index.js.map +1 -7
- package/dist/ipld.d.ts +3 -2
- package/dist/ipld.d.ts.map +1 -0
- package/dist/ipld.js +119 -0
- package/dist/ipld.js.map +1 -0
- package/dist/retry.d.ts +8 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +46 -0
- package/dist/retry.js.map +1 -0
- package/dist/strings.d.ts +2 -1
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +74 -0
- package/dist/strings.js.map +1 -0
- package/dist/tid.d.ts +1 -0
- package/dist/tid.d.ts.map +1 -0
- package/dist/tid.js +100 -0
- package/dist/tid.js.map +1 -0
- package/dist/times.d.ts +2 -0
- package/dist/times.d.ts.map +1 -0
- package/dist/times.js +19 -0
- package/dist/times.js.map +1 -0
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +42 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +3 -2
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +114 -0
- package/dist/util.js.map +1 -0
- package/jest.config.js +3 -3
- package/package.json +7 -6
- package/src/arrays.ts +7 -0
- package/src/async.ts +27 -0
- package/src/did-doc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/retry.ts +52 -0
- package/src/times.ts +7 -0
- package/src/util.ts +2 -2
- package/tests/retry.test.ts +93 -0
- package/tsconfig.build.json +6 -2
- package/tsconfig.json +5 -7
- package/tsconfig.tests.json +7 -0
- package/babel.config.js +0 -1
- package/build.js +0 -15
package/dist/util.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseIntWithFallback = exports.dedupeStrs = exports.range = exports.chunkArray = exports.errHasMsg = exports.isErrnoException = exports.asyncFilter = exports.s32decode = exports.s32encode = exports.streamToBuffer = exports.flattenUint8Arrays = exports.bailableWait = exports.wait = exports.jitter = exports.noUndefinedVals = void 0;
|
|
4
|
+
const noUndefinedVals = (obj) => {
|
|
5
|
+
Object.keys(obj).forEach((k) => {
|
|
6
|
+
if (obj[k] === undefined) {
|
|
7
|
+
delete obj[k];
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
return obj;
|
|
11
|
+
};
|
|
12
|
+
exports.noUndefinedVals = noUndefinedVals;
|
|
13
|
+
const jitter = (maxMs) => {
|
|
14
|
+
return Math.round((Math.random() - 0.5) * maxMs * 2);
|
|
15
|
+
};
|
|
16
|
+
exports.jitter = jitter;
|
|
17
|
+
const wait = (ms) => {
|
|
18
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
19
|
+
};
|
|
20
|
+
exports.wait = wait;
|
|
21
|
+
const bailableWait = (ms) => {
|
|
22
|
+
let bail;
|
|
23
|
+
const waitPromise = new Promise((res) => {
|
|
24
|
+
const timeout = setTimeout(res, ms);
|
|
25
|
+
bail = () => {
|
|
26
|
+
clearTimeout(timeout);
|
|
27
|
+
res();
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
return { bail, wait: () => waitPromise };
|
|
31
|
+
};
|
|
32
|
+
exports.bailableWait = bailableWait;
|
|
33
|
+
const flattenUint8Arrays = (arrs) => {
|
|
34
|
+
const length = arrs.reduce((acc, cur) => {
|
|
35
|
+
return acc + cur.length;
|
|
36
|
+
}, 0);
|
|
37
|
+
const flattened = new Uint8Array(length);
|
|
38
|
+
let offset = 0;
|
|
39
|
+
arrs.forEach((arr) => {
|
|
40
|
+
flattened.set(arr, offset);
|
|
41
|
+
offset += arr.length;
|
|
42
|
+
});
|
|
43
|
+
return flattened;
|
|
44
|
+
};
|
|
45
|
+
exports.flattenUint8Arrays = flattenUint8Arrays;
|
|
46
|
+
const streamToBuffer = async (stream) => {
|
|
47
|
+
const arrays = [];
|
|
48
|
+
for await (const chunk of stream) {
|
|
49
|
+
arrays.push(chunk);
|
|
50
|
+
}
|
|
51
|
+
return (0, exports.flattenUint8Arrays)(arrays);
|
|
52
|
+
};
|
|
53
|
+
exports.streamToBuffer = streamToBuffer;
|
|
54
|
+
const S32_CHAR = '234567abcdefghijklmnopqrstuvwxyz';
|
|
55
|
+
const s32encode = (i) => {
|
|
56
|
+
let s = '';
|
|
57
|
+
while (i) {
|
|
58
|
+
const c = i % 32;
|
|
59
|
+
i = Math.floor(i / 32);
|
|
60
|
+
s = S32_CHAR.charAt(c) + s;
|
|
61
|
+
}
|
|
62
|
+
return s;
|
|
63
|
+
};
|
|
64
|
+
exports.s32encode = s32encode;
|
|
65
|
+
const s32decode = (s) => {
|
|
66
|
+
let i = 0;
|
|
67
|
+
for (const c of s) {
|
|
68
|
+
i = i * 32 + S32_CHAR.indexOf(c);
|
|
69
|
+
}
|
|
70
|
+
return i;
|
|
71
|
+
};
|
|
72
|
+
exports.s32decode = s32decode;
|
|
73
|
+
const asyncFilter = async (arr, fn) => {
|
|
74
|
+
const results = await Promise.all(arr.map((t) => fn(t)));
|
|
75
|
+
return arr.filter((_, i) => results[i]);
|
|
76
|
+
};
|
|
77
|
+
exports.asyncFilter = asyncFilter;
|
|
78
|
+
const isErrnoException = (err) => {
|
|
79
|
+
return !!err && err['code'];
|
|
80
|
+
};
|
|
81
|
+
exports.isErrnoException = isErrnoException;
|
|
82
|
+
const errHasMsg = (err, msg) => {
|
|
83
|
+
return !!err && typeof err === 'object' && err['message'] === msg;
|
|
84
|
+
};
|
|
85
|
+
exports.errHasMsg = errHasMsg;
|
|
86
|
+
const chunkArray = (arr, chunkSize) => {
|
|
87
|
+
return arr.reduce((acc, cur, i) => {
|
|
88
|
+
const chunkI = Math.floor(i / chunkSize);
|
|
89
|
+
if (!acc[chunkI]) {
|
|
90
|
+
acc[chunkI] = [];
|
|
91
|
+
}
|
|
92
|
+
acc[chunkI].push(cur);
|
|
93
|
+
return acc;
|
|
94
|
+
}, []);
|
|
95
|
+
};
|
|
96
|
+
exports.chunkArray = chunkArray;
|
|
97
|
+
const range = (num) => {
|
|
98
|
+
const nums = [];
|
|
99
|
+
for (let i = 0; i < num; i++) {
|
|
100
|
+
nums.push(i);
|
|
101
|
+
}
|
|
102
|
+
return nums;
|
|
103
|
+
};
|
|
104
|
+
exports.range = range;
|
|
105
|
+
const dedupeStrs = (strs) => {
|
|
106
|
+
return [...new Set(strs)];
|
|
107
|
+
};
|
|
108
|
+
exports.dedupeStrs = dedupeStrs;
|
|
109
|
+
const parseIntWithFallback = (value, fallback) => {
|
|
110
|
+
const parsed = parseInt(value || '', 10);
|
|
111
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
112
|
+
};
|
|
113
|
+
exports.parseIntWithFallback = parseIntWithFallback;
|
|
114
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/common-web",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4-next.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Shared web-platform-friendly code for atproto libraries",
|
|
6
6
|
"keywords": [
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
"directory": "packages/common-web"
|
|
14
14
|
},
|
|
15
15
|
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
16
17
|
"dependencies": {
|
|
17
18
|
"graphemer": "^1.4.0",
|
|
18
19
|
"multiformats": "^9.9.0",
|
|
19
20
|
"uint8arrays": "3.0.0",
|
|
20
21
|
"zod": "^3.21.4"
|
|
21
22
|
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"jest": "^28.1.2"
|
|
25
|
+
},
|
|
22
26
|
"scripts": {
|
|
23
27
|
"test": "jest",
|
|
24
|
-
"build": "
|
|
25
|
-
|
|
26
|
-
"update-main-to-dist": "node ../../update-main-to-dist.js packages/common-web"
|
|
27
|
-
},
|
|
28
|
-
"types": "dist/index.d.ts"
|
|
28
|
+
"build": "tsc --build tsconfig.build.json"
|
|
29
|
+
}
|
|
29
30
|
}
|
package/src/arrays.ts
CHANGED
package/src/async.ts
CHANGED
|
@@ -72,6 +72,8 @@ export class AsyncBuffer<T> {
|
|
|
72
72
|
private buffer: T[] = []
|
|
73
73
|
private promise: Promise<void>
|
|
74
74
|
private resolve: () => void
|
|
75
|
+
private closed = false
|
|
76
|
+
private toThrow: unknown | undefined
|
|
75
77
|
|
|
76
78
|
constructor(public maxSize?: number) {
|
|
77
79
|
// Initializing to satisfy types/build, immediately reset by resetPromise()
|
|
@@ -88,6 +90,10 @@ export class AsyncBuffer<T> {
|
|
|
88
90
|
return this.buffer.length
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
get isClosed(): boolean {
|
|
94
|
+
return this.closed
|
|
95
|
+
}
|
|
96
|
+
|
|
91
97
|
resetPromise() {
|
|
92
98
|
this.promise = new Promise<void>((r) => (this.resolve = r))
|
|
93
99
|
}
|
|
@@ -104,7 +110,17 @@ export class AsyncBuffer<T> {
|
|
|
104
110
|
|
|
105
111
|
async *events(): AsyncGenerator<T> {
|
|
106
112
|
while (true) {
|
|
113
|
+
if (this.closed && this.buffer.length === 0) {
|
|
114
|
+
if (this.toThrow) {
|
|
115
|
+
throw this.toThrow
|
|
116
|
+
} else {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
}
|
|
107
120
|
await this.promise
|
|
121
|
+
if (this.toThrow) {
|
|
122
|
+
throw this.toThrow
|
|
123
|
+
}
|
|
108
124
|
if (this.maxSize && this.size > this.maxSize) {
|
|
109
125
|
throw new AsyncBufferFullError(this.maxSize)
|
|
110
126
|
}
|
|
@@ -117,6 +133,17 @@ export class AsyncBuffer<T> {
|
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
}
|
|
136
|
+
|
|
137
|
+
throw(err: unknown) {
|
|
138
|
+
this.toThrow = err
|
|
139
|
+
this.closed = true
|
|
140
|
+
this.resolve()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
close() {
|
|
144
|
+
this.closed = true
|
|
145
|
+
this.resolve()
|
|
146
|
+
}
|
|
120
147
|
}
|
|
121
148
|
|
|
122
149
|
export class AsyncBufferFullError extends Error {
|
package/src/did-doc.ts
CHANGED
|
@@ -44,6 +44,11 @@ export const getSigningKey = (
|
|
|
44
44
|
publicKeyMultibase: found.publicKeyMultibase,
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
export const getSigningDidKey = (doc: DidDocument): string | undefined => {
|
|
48
|
+
const parsed = getSigningKey(doc)
|
|
49
|
+
if (!parsed) return
|
|
50
|
+
return `did:key:${parsed.publicKeyMultibase}`
|
|
51
|
+
}
|
|
47
52
|
|
|
48
53
|
export const getPdsEndpoint = (doc: DidDocument): string | undefined => {
|
|
49
54
|
return getServiceEndpoint(doc, {
|
package/src/index.ts
CHANGED
package/src/retry.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { wait } from './util'
|
|
2
|
+
|
|
3
|
+
export type RetryOptions = {
|
|
4
|
+
maxRetries?: number
|
|
5
|
+
getWaitMs?: (n: number) => number | null
|
|
6
|
+
retryable?: (err: unknown) => boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function retry<T>(
|
|
10
|
+
fn: () => Promise<T>,
|
|
11
|
+
opts: RetryOptions = {},
|
|
12
|
+
): Promise<T> {
|
|
13
|
+
const { maxRetries = 3, retryable = () => true, getWaitMs = backoffMs } = opts
|
|
14
|
+
let retries = 0
|
|
15
|
+
let doneError: unknown
|
|
16
|
+
while (!doneError) {
|
|
17
|
+
try {
|
|
18
|
+
return await fn()
|
|
19
|
+
} catch (err) {
|
|
20
|
+
const waitMs = getWaitMs(retries)
|
|
21
|
+
const willRetry =
|
|
22
|
+
retries < maxRetries && waitMs !== null && retryable(err)
|
|
23
|
+
if (willRetry) {
|
|
24
|
+
retries += 1
|
|
25
|
+
if (waitMs !== 0) {
|
|
26
|
+
await wait(waitMs)
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
doneError = err
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw doneError
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Waits exponential backoff with max and jitter: ~100, ~200, ~400, ~800, ~1000, ~1000, ...
|
|
37
|
+
export function backoffMs(n: number, multiplier = 100, max = 1000) {
|
|
38
|
+
const exponentialMs = Math.pow(2, n) * multiplier
|
|
39
|
+
const ms = Math.min(exponentialMs, max)
|
|
40
|
+
return jitter(ms)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Adds randomness +/-15% of value
|
|
44
|
+
function jitter(value: number) {
|
|
45
|
+
const delta = value * 0.15
|
|
46
|
+
return value + randomRange(-delta, delta)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function randomRange(from: number, to: number) {
|
|
50
|
+
const rand = Math.random() * (to - from)
|
|
51
|
+
return rand + from
|
|
52
|
+
}
|
package/src/times.ts
CHANGED
|
@@ -6,3 +6,10 @@ export const DAY = HOUR * 24
|
|
|
6
6
|
export const lessThanAgoMs = (time: Date, range: number) => {
|
|
7
7
|
return Date.now() < time.getTime() + range
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
export const addHoursToDate = (hours: number, startingDate?: Date): Date => {
|
|
11
|
+
// When date is passed, clone before calling `setHours()` so that we are not mutating the original date
|
|
12
|
+
const currentDate = startingDate ? new Date(startingDate) : new Date()
|
|
13
|
+
currentDate.setHours(currentDate.getHours() + hours)
|
|
14
|
+
return currentDate
|
|
15
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const noUndefinedVals = <T>(
|
|
2
|
-
obj: Record<string, T>,
|
|
2
|
+
obj: Record<string, T | undefined>,
|
|
3
3
|
): Record<string, T> => {
|
|
4
4
|
Object.keys(obj).forEach((k) => {
|
|
5
5
|
if (obj[k] === undefined) {
|
|
6
6
|
delete obj[k]
|
|
7
7
|
}
|
|
8
8
|
})
|
|
9
|
-
return obj
|
|
9
|
+
return obj as Record<string, T>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export const jitter = (maxMs: number) => {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { retry } from '../src/index'
|
|
2
|
+
|
|
3
|
+
describe('retry', () => {
|
|
4
|
+
describe('retry()', () => {
|
|
5
|
+
it('retries until max retries', async () => {
|
|
6
|
+
let fnCalls = 0
|
|
7
|
+
let waitMsCalls = 0
|
|
8
|
+
const fn = async () => {
|
|
9
|
+
fnCalls++
|
|
10
|
+
throw new Error(`Oops ${fnCalls}!`)
|
|
11
|
+
}
|
|
12
|
+
const getWaitMs = (retries) => {
|
|
13
|
+
waitMsCalls++
|
|
14
|
+
expect(retries).toEqual(waitMsCalls - 1)
|
|
15
|
+
return 0
|
|
16
|
+
}
|
|
17
|
+
await expect(retry(fn, { maxRetries: 13, getWaitMs })).rejects.toThrow(
|
|
18
|
+
'Oops 14!',
|
|
19
|
+
)
|
|
20
|
+
expect(fnCalls).toEqual(14)
|
|
21
|
+
expect(waitMsCalls).toEqual(14)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('retries until max wait', async () => {
|
|
25
|
+
let fnCalls = 0
|
|
26
|
+
let waitMsCalls = 0
|
|
27
|
+
const fn = async () => {
|
|
28
|
+
fnCalls++
|
|
29
|
+
throw new Error(`Oops ${fnCalls}!`)
|
|
30
|
+
}
|
|
31
|
+
const getWaitMs = (retries) => {
|
|
32
|
+
waitMsCalls++
|
|
33
|
+
expect(retries).toEqual(waitMsCalls - 1)
|
|
34
|
+
if (retries === 13) {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
return 0
|
|
38
|
+
}
|
|
39
|
+
await expect(
|
|
40
|
+
retry(fn, { maxRetries: Infinity, getWaitMs }),
|
|
41
|
+
).rejects.toThrow('Oops 14!')
|
|
42
|
+
expect(fnCalls).toEqual(14)
|
|
43
|
+
expect(waitMsCalls).toEqual(14)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('retries until non-retryable error', async () => {
|
|
47
|
+
let fnCalls = 0
|
|
48
|
+
let waitMsCalls = 0
|
|
49
|
+
const fn = async () => {
|
|
50
|
+
fnCalls++
|
|
51
|
+
throw new Error(`Oops ${fnCalls}!`)
|
|
52
|
+
}
|
|
53
|
+
const getWaitMs = (retries) => {
|
|
54
|
+
waitMsCalls++
|
|
55
|
+
expect(retries).toEqual(waitMsCalls - 1)
|
|
56
|
+
return 0
|
|
57
|
+
}
|
|
58
|
+
const retryable = (err: unknown) => err?.['message'] !== 'Oops 14!'
|
|
59
|
+
await expect(
|
|
60
|
+
retry(fn, { maxRetries: Infinity, getWaitMs, retryable }),
|
|
61
|
+
).rejects.toThrow('Oops 14!')
|
|
62
|
+
expect(fnCalls).toEqual(14)
|
|
63
|
+
expect(waitMsCalls).toEqual(14)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('returns latest result after retries', async () => {
|
|
67
|
+
let fnCalls = 0
|
|
68
|
+
const fn = async () => {
|
|
69
|
+
fnCalls++
|
|
70
|
+
if (fnCalls < 14) {
|
|
71
|
+
throw new Error(`Oops ${fnCalls}!`)
|
|
72
|
+
}
|
|
73
|
+
return 'ok'
|
|
74
|
+
}
|
|
75
|
+
const getWaitMs = () => 0
|
|
76
|
+
const result = await retry(fn, { maxRetries: Infinity, getWaitMs })
|
|
77
|
+
expect(result).toBe('ok')
|
|
78
|
+
expect(fnCalls).toBe(14)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('returns result immediately on success', async () => {
|
|
82
|
+
let fnCalls = 0
|
|
83
|
+
const fn = async () => {
|
|
84
|
+
fnCalls++
|
|
85
|
+
return 'ok'
|
|
86
|
+
}
|
|
87
|
+
const getWaitMs = () => 0
|
|
88
|
+
const result = await retry(fn, { maxRetries: Infinity, getWaitMs })
|
|
89
|
+
expect(result).toBe('ok')
|
|
90
|
+
expect(fnCalls).toBe(1)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
})
|
package/tsconfig.build.json
CHANGED
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
"include": ["./src", "__tests__/**/**.ts"]
|
|
2
|
+
"include": [],
|
|
3
|
+
"references": [
|
|
4
|
+
{ "path": "./tsconfig.build.json" },
|
|
5
|
+
{ "path": "./tsconfig.tests.json" }
|
|
6
|
+
]
|
|
9
7
|
}
|
package/babel.config.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('../../babel.config.js')
|
package/build.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const { nodeExternalsPlugin } = require('esbuild-node-externals')
|
|
2
|
-
|
|
3
|
-
const buildShallow =
|
|
4
|
-
process.argv.includes('--shallow') || process.env.ATP_BUILD_SHALLOW === 'true'
|
|
5
|
-
|
|
6
|
-
require('esbuild').build({
|
|
7
|
-
logLevel: 'info',
|
|
8
|
-
entryPoints: ['src/index.ts'],
|
|
9
|
-
bundle: true,
|
|
10
|
-
sourcemap: true,
|
|
11
|
-
outdir: 'dist',
|
|
12
|
-
platform: 'browser',
|
|
13
|
-
format: 'cjs',
|
|
14
|
-
plugins: buildShallow ? [nodeExternalsPlugin()] : [],
|
|
15
|
-
})
|