@atproto/common-web 0.2.2 → 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 +12 -0
- package/LICENSE.txt +7 -0
- package/README.md +6 -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/LICENSE +0 -21
- 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/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022-2023 Bluesky PBC
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
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
|
-
})
|