@anvilkit/plugin-asset-manager 0.1.1
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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/adapters/data-url.cjs +78 -0
- package/dist/adapters/data-url.d.cts +6 -0
- package/dist/adapters/data-url.d.cts.map +1 -0
- package/dist/adapters/data-url.d.ts +6 -0
- package/dist/adapters/data-url.d.ts.map +1 -0
- package/dist/adapters/data-url.js +44 -0
- package/dist/adapters/extract-image-dimensions.cjs +69 -0
- package/dist/adapters/extract-image-dimensions.d.cts +21 -0
- package/dist/adapters/extract-image-dimensions.d.cts.map +1 -0
- package/dist/adapters/extract-image-dimensions.d.ts +21 -0
- package/dist/adapters/extract-image-dimensions.d.ts.map +1 -0
- package/dist/adapters/extract-image-dimensions.js +35 -0
- package/dist/adapters/in-memory.cjs +62 -0
- package/dist/adapters/in-memory.d.cts +3 -0
- package/dist/adapters/in-memory.d.cts.map +1 -0
- package/dist/adapters/in-memory.d.ts +3 -0
- package/dist/adapters/in-memory.d.ts.map +1 -0
- package/dist/adapters/in-memory.js +28 -0
- package/dist/adapters/s3-presigned.cjs +166 -0
- package/dist/adapters/s3-presigned.d.cts +59 -0
- package/dist/adapters/s3-presigned.d.cts.map +1 -0
- package/dist/adapters/s3-presigned.d.ts +59 -0
- package/dist/adapters/s3-presigned.d.ts.map +1 -0
- package/dist/adapters/s3-presigned.js +129 -0
- package/dist/asset-reference.cjs +38 -0
- package/dist/asset-reference.d.cts +10 -0
- package/dist/asset-reference.d.cts.map +1 -0
- package/dist/asset-reference.d.ts +10 -0
- package/dist/asset-reference.d.ts.map +1 -0
- package/dist/asset-reference.js +4 -0
- package/dist/csp.cjs +83 -0
- package/dist/csp.d.cts +45 -0
- package/dist/csp.d.cts.map +1 -0
- package/dist/csp.d.ts +45 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +49 -0
- package/dist/errors.cjs +58 -0
- package/dist/errors.d.cts +15 -0
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/header-action.cjs +44 -0
- package/dist/header-action.d.cts +3 -0
- package/dist/header-action.d.cts.map +1 -0
- package/dist/header-action.d.ts +3 -0
- package/dist/header-action.d.ts.map +1 -0
- package/dist/header-action.js +10 -0
- package/dist/index.cjs +90 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/infer-kind.cjs +45 -0
- package/dist/infer-kind.d.cts +8 -0
- package/dist/infer-kind.d.cts.map +1 -0
- package/dist/infer-kind.d.ts +8 -0
- package/dist/infer-kind.d.ts.map +1 -0
- package/dist/infer-kind.js +11 -0
- package/dist/plugin.cjs +258 -0
- package/dist/plugin.d.cts +9 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +212 -0
- package/dist/registry.cjs +198 -0
- package/dist/registry.d.cts +3 -0
- package/dist/registry.d.cts.map +1 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +164 -0
- package/dist/resolver.cjs +185 -0
- package/dist/resolver.d.cts +10 -0
- package/dist/resolver.d.cts.map +1 -0
- package/dist/resolver.d.ts +10 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +148 -0
- package/dist/retry.cjs +123 -0
- package/dist/retry.d.cts +71 -0
- package/dist/retry.d.cts.map +1 -0
- package/dist/retry.d.ts +71 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +86 -0
- package/dist/studio-asset-source.cjs +211 -0
- package/dist/studio-asset-source.d.cts +52 -0
- package/dist/studio-asset-source.d.cts.map +1 -0
- package/dist/studio-asset-source.d.ts +52 -0
- package/dist/studio-asset-source.d.ts.map +1 -0
- package/dist/studio-asset-source.js +171 -0
- package/dist/testing/index.cjs +66 -0
- package/dist/testing/index.d.cts +24 -0
- package/dist/testing/index.d.cts.map +1 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +29 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +132 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/ui/AssetBrowser.cjs +271 -0
- package/dist/ui/AssetBrowser.d.cts +45 -0
- package/dist/ui/AssetBrowser.d.cts.map +1 -0
- package/dist/ui/AssetBrowser.d.ts +45 -0
- package/dist/ui/AssetBrowser.d.ts.map +1 -0
- package/dist/ui/AssetBrowser.js +237 -0
- package/dist/ui/AssetCommandPalette.cjs +135 -0
- package/dist/ui/AssetCommandPalette.d.cts +21 -0
- package/dist/ui/AssetCommandPalette.d.cts.map +1 -0
- package/dist/ui/AssetCommandPalette.d.ts +21 -0
- package/dist/ui/AssetCommandPalette.d.ts.map +1 -0
- package/dist/ui/AssetCommandPalette.js +101 -0
- package/dist/ui/AssetManagerUI.cjs +169 -0
- package/dist/ui/AssetManagerUI.d.cts +15 -0
- package/dist/ui/AssetManagerUI.d.cts.map +1 -0
- package/dist/ui/AssetManagerUI.d.ts +15 -0
- package/dist/ui/AssetManagerUI.d.ts.map +1 -0
- package/dist/ui/AssetManagerUI.js +135 -0
- package/dist/ui/DeleteAssetDialog.cjs +70 -0
- package/dist/ui/DeleteAssetDialog.d.cts +22 -0
- package/dist/ui/DeleteAssetDialog.d.cts.map +1 -0
- package/dist/ui/DeleteAssetDialog.d.ts +22 -0
- package/dist/ui/DeleteAssetDialog.d.ts.map +1 -0
- package/dist/ui/DeleteAssetDialog.js +36 -0
- package/dist/ui/MetadataPanel.cjs +147 -0
- package/dist/ui/MetadataPanel.d.cts +21 -0
- package/dist/ui/MetadataPanel.d.cts.map +1 -0
- package/dist/ui/MetadataPanel.d.ts +21 -0
- package/dist/ui/MetadataPanel.d.ts.map +1 -0
- package/dist/ui/MetadataPanel.js +113 -0
- package/dist/ui/ReplaceAssetDialog.cjs +125 -0
- package/dist/ui/ReplaceAssetDialog.d.cts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.d.ts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.js +91 -0
- package/dist/ui/UploadButton.cjs +189 -0
- package/dist/ui/UploadButton.d.cts +17 -0
- package/dist/ui/UploadButton.d.cts.map +1 -0
- package/dist/ui/UploadButton.d.ts +17 -0
- package/dist/ui/UploadButton.d.ts.map +1 -0
- package/dist/ui/UploadButton.js +155 -0
- package/dist/ui/index.cjs +60 -0
- package/dist/ui/index.d.cts +15 -0
- package/dist/ui/index.d.cts.map +1 -0
- package/dist/ui/index.d.ts +15 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/validate-upload-result.cjs +149 -0
- package/dist/validate-upload-result.d.cts +9 -0
- package/dist/validate-upload-result.d.cts.map +1 -0
- package/dist/validate-upload-result.d.ts +9 -0
- package/dist/validate-upload-result.d.ts.map +1 -0
- package/dist/validate-upload-result.js +115 -0
- package/package.json +131 -0
package/dist/resolver.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { AssetResolutionError, AssetValidationError } from "./errors.js";
|
|
2
|
+
import { validateUploadResult } from "./validate-upload-result.js";
|
|
3
|
+
const URL_REJECTION_VALIDATION_CODES = new Set([
|
|
4
|
+
"EMPTY_UPLOAD_URL",
|
|
5
|
+
"UNSCHEMED_UPLOAD_URL",
|
|
6
|
+
"DISALLOWED_UPLOAD_URL_SCHEME",
|
|
7
|
+
"INVALID_UPLOAD_ID",
|
|
8
|
+
"PATH_TRAVERSAL_URL",
|
|
9
|
+
"MIXED_SCRIPT_HOSTNAME"
|
|
10
|
+
]);
|
|
11
|
+
const ASSET_REFERENCE_PREFIX = "asset://";
|
|
12
|
+
const ASSET_PROP_KEYS = new Set([
|
|
13
|
+
"src",
|
|
14
|
+
"imageUrl",
|
|
15
|
+
"imageSrc",
|
|
16
|
+
"url",
|
|
17
|
+
"videoUrl",
|
|
18
|
+
"videoSrc",
|
|
19
|
+
"fontUrl",
|
|
20
|
+
"scriptUrl",
|
|
21
|
+
"styleUrl",
|
|
22
|
+
"backgroundSrc",
|
|
23
|
+
"backgroundImage",
|
|
24
|
+
"poster",
|
|
25
|
+
"thumbnailSrc"
|
|
26
|
+
]);
|
|
27
|
+
function createIRAssetResolver(options) {
|
|
28
|
+
return (url)=>{
|
|
29
|
+
const assetId = parseAssetReference(url);
|
|
30
|
+
if (null === assetId) return null;
|
|
31
|
+
const asset = options.registry.get(assetId);
|
|
32
|
+
if (!asset) throw new AssetResolutionError(assetId, "ASSET_NOT_FOUND");
|
|
33
|
+
try {
|
|
34
|
+
const validated = validateUploadResult({
|
|
35
|
+
id: asset.id,
|
|
36
|
+
url: asset.url,
|
|
37
|
+
...asset.meta ? {
|
|
38
|
+
meta: asset.meta
|
|
39
|
+
} : {}
|
|
40
|
+
}, {
|
|
41
|
+
dataUrlAllowlistOptIn: options.dataUrlAllowlistOptIn,
|
|
42
|
+
allowMixedScriptHostnames: options.allowMixedScriptHostnames
|
|
43
|
+
});
|
|
44
|
+
const resolution = {
|
|
45
|
+
url: validated.url,
|
|
46
|
+
...validated.meta ? {
|
|
47
|
+
meta: validated.meta
|
|
48
|
+
} : {}
|
|
49
|
+
};
|
|
50
|
+
return resolution;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const message = error instanceof Error ? error.message : `Could not resolve asset "${assetId}"`;
|
|
53
|
+
const code = error instanceof AssetValidationError && URL_REJECTION_VALIDATION_CODES.has(error.code) ? "ASSET_URL_REJECTED" : "ASSET_VALIDATION_FAILED";
|
|
54
|
+
throw new AssetResolutionError(assetId, code, message, {
|
|
55
|
+
cause: error
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function resolveAssets(ir, resolver) {
|
|
61
|
+
const rewriteMap = new Map();
|
|
62
|
+
const assetUrls = collectAssetUrls(ir);
|
|
63
|
+
for (const url of assetUrls){
|
|
64
|
+
const resolution = await resolver(url);
|
|
65
|
+
if (null !== resolution) rewriteMap.set(url, resolution);
|
|
66
|
+
}
|
|
67
|
+
const nextIr = {
|
|
68
|
+
version: ir.version,
|
|
69
|
+
root: cloneNode(ir.root, rewriteMap),
|
|
70
|
+
assets: ir.assets.map((asset)=>cloneAsset(asset, rewriteMap)),
|
|
71
|
+
metadata: {
|
|
72
|
+
...ir.metadata
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return deepFreeze(nextIr);
|
|
76
|
+
}
|
|
77
|
+
function collectAssetUrls(ir) {
|
|
78
|
+
const urls = new Set();
|
|
79
|
+
for (const asset of ir.assets)if ("" !== asset.url.trim()) urls.add(asset.url);
|
|
80
|
+
collectNodeAssetUrls(ir.root, urls);
|
|
81
|
+
return urls;
|
|
82
|
+
}
|
|
83
|
+
function collectNodeAssetUrls(node, urls) {
|
|
84
|
+
collectValueAssetUrls(node.props, urls);
|
|
85
|
+
if (node.assets) {
|
|
86
|
+
for (const asset of node.assets)if ("" !== asset.url.trim()) urls.add(asset.url);
|
|
87
|
+
}
|
|
88
|
+
if (node.children) for (const child of node.children)collectNodeAssetUrls(child, urls);
|
|
89
|
+
}
|
|
90
|
+
function collectValueAssetUrls(value, urls, key) {
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
for (const item of value)collectValueAssetUrls(item, urls);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if ("string" == typeof value) {
|
|
96
|
+
if (void 0 !== key && ASSET_PROP_KEYS.has(key) && "" !== value.trim()) urls.add(value);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (null === value || "object" != typeof value) return;
|
|
100
|
+
for (const [entryKey, entryValue] of Object.entries(value))collectValueAssetUrls(entryValue, urls, entryKey);
|
|
101
|
+
}
|
|
102
|
+
function parseAssetReference(url) {
|
|
103
|
+
if (!url.startsWith(ASSET_REFERENCE_PREFIX)) return null;
|
|
104
|
+
const assetId = url.slice(ASSET_REFERENCE_PREFIX.length).trim();
|
|
105
|
+
return "" === assetId ? null : assetId;
|
|
106
|
+
}
|
|
107
|
+
function cloneNode(node, rewriteMap) {
|
|
108
|
+
return {
|
|
109
|
+
id: node.id,
|
|
110
|
+
type: node.type,
|
|
111
|
+
props: cloneValue(node.props, rewriteMap),
|
|
112
|
+
...node.children ? {
|
|
113
|
+
children: node.children.map((child)=>cloneNode(child, rewriteMap))
|
|
114
|
+
} : {},
|
|
115
|
+
...node.assets ? {
|
|
116
|
+
assets: node.assets.map((asset)=>cloneAsset(asset, rewriteMap))
|
|
117
|
+
} : {}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function cloneAsset(asset, rewriteMap) {
|
|
121
|
+
const resolution = rewriteMap.get(asset.url);
|
|
122
|
+
return {
|
|
123
|
+
id: asset.id,
|
|
124
|
+
kind: asset.kind,
|
|
125
|
+
url: resolution?.url ?? asset.url,
|
|
126
|
+
...resolution?.meta ?? asset.meta ? {
|
|
127
|
+
meta: resolution?.meta ?? asset.meta
|
|
128
|
+
} : {}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function cloneValue(value, rewriteMap, key) {
|
|
132
|
+
if (Array.isArray(value)) return value.map((item)=>cloneValue(item, rewriteMap));
|
|
133
|
+
if ("string" == typeof value) {
|
|
134
|
+
if (void 0 !== key && !ASSET_PROP_KEYS.has(key)) return value;
|
|
135
|
+
return rewriteMap.get(value)?.url ?? value;
|
|
136
|
+
}
|
|
137
|
+
if (null === value || "object" != typeof value) return value;
|
|
138
|
+
const nextValue = {};
|
|
139
|
+
for (const [entryKey, entryValue] of Object.entries(value))nextValue[entryKey] = cloneValue(entryValue, rewriteMap, entryKey);
|
|
140
|
+
return nextValue;
|
|
141
|
+
}
|
|
142
|
+
function deepFreeze(value) {
|
|
143
|
+
if (null === value || "object" != typeof value || Object.isFrozen(value)) return value;
|
|
144
|
+
Object.freeze(value);
|
|
145
|
+
for (const entry of Object.values(value))deepFreeze(entry);
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
export { createIRAssetResolver, resolveAssets };
|
package/dist/retry.cjs
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
RetryableError: ()=>RetryableError,
|
|
28
|
+
withRetry: ()=>withRetry
|
|
29
|
+
});
|
|
30
|
+
class RetryableError extends Error {
|
|
31
|
+
retryAfterMs;
|
|
32
|
+
constructor(message, options){
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "RetryableError";
|
|
35
|
+
if (options && "cause" in options) this.cause = options.cause;
|
|
36
|
+
if (options?.retryAfterMs !== void 0) this.retryAfterMs = options.retryAfterMs;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
40
|
+
const DEFAULT_BASE_DELAY_MS = 250;
|
|
41
|
+
const DEFAULT_MAX_DELAY_MS = 8000;
|
|
42
|
+
async function withRetry(fn, options = {}) {
|
|
43
|
+
const { maxRetries = DEFAULT_MAX_RETRIES, baseDelayMs = DEFAULT_BASE_DELAY_MS, maxDelayMs = DEFAULT_MAX_DELAY_MS, signal, jitter = Math.random, sleep = defaultSleep } = options;
|
|
44
|
+
let attempt = 0;
|
|
45
|
+
while(true){
|
|
46
|
+
throwIfAborted(signal);
|
|
47
|
+
try {
|
|
48
|
+
return await fn(attempt);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throwIfAborted(signal);
|
|
51
|
+
if (!isRetryable(error)) throw error;
|
|
52
|
+
if (attempt >= maxRetries) throw error;
|
|
53
|
+
const delay = computeDelay({
|
|
54
|
+
attempt,
|
|
55
|
+
baseDelayMs,
|
|
56
|
+
maxDelayMs,
|
|
57
|
+
jitter,
|
|
58
|
+
retryAfterMs: getRetryAfterMs(error)
|
|
59
|
+
});
|
|
60
|
+
await sleep(delay, signal);
|
|
61
|
+
attempt += 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function isRetryable(error) {
|
|
66
|
+
if (error instanceof RetryableError) return true;
|
|
67
|
+
return null !== error && "object" == typeof error && "RetryableError" === error.name;
|
|
68
|
+
}
|
|
69
|
+
function getRetryAfterMs(error) {
|
|
70
|
+
if (null !== error && "object" == typeof error && "number" == typeof error.retryAfterMs) return error.retryAfterMs;
|
|
71
|
+
}
|
|
72
|
+
function computeDelay(input) {
|
|
73
|
+
if (void 0 !== input.retryAfterMs) return Math.max(0, input.retryAfterMs);
|
|
74
|
+
const exp = Math.min(input.maxDelayMs, input.baseDelayMs * 2 ** input.attempt);
|
|
75
|
+
const half = exp / 2;
|
|
76
|
+
return Math.floor(half + input.jitter() * half);
|
|
77
|
+
}
|
|
78
|
+
function throwIfAborted(signal) {
|
|
79
|
+
if (signal?.aborted) throw makeAbortError(signal);
|
|
80
|
+
}
|
|
81
|
+
function makeAbortError(signal) {
|
|
82
|
+
const reason = signal.reason;
|
|
83
|
+
if (reason instanceof Error) return reason;
|
|
84
|
+
if ("u" > typeof DOMException) return new DOMException("Aborted", "AbortError");
|
|
85
|
+
const error = new Error("Aborted");
|
|
86
|
+
error.name = "AbortError";
|
|
87
|
+
return error;
|
|
88
|
+
}
|
|
89
|
+
function defaultSleep(ms, signal) {
|
|
90
|
+
if (ms <= 0) {
|
|
91
|
+
throwIfAborted(signal);
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
}
|
|
94
|
+
return new Promise((resolve, reject)=>{
|
|
95
|
+
const timer = setTimeout(()=>{
|
|
96
|
+
signal?.removeEventListener("abort", onAbort);
|
|
97
|
+
resolve();
|
|
98
|
+
}, ms);
|
|
99
|
+
const onAbort = ()=>{
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
reject(makeAbortError(signal));
|
|
102
|
+
};
|
|
103
|
+
if (signal) {
|
|
104
|
+
if (signal.aborted) {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
reject(makeAbortError(signal));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
signal.addEventListener("abort", onAbort, {
|
|
110
|
+
once: true
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
exports.RetryableError = __webpack_exports__.RetryableError;
|
|
116
|
+
exports.withRetry = __webpack_exports__.withRetry;
|
|
117
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
118
|
+
"RetryableError",
|
|
119
|
+
"withRetry"
|
|
120
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
121
|
+
Object.defineProperty(exports, '__esModule', {
|
|
122
|
+
value: true
|
|
123
|
+
});
|
package/dist/retry.d.cts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Retry helpers used by network-bound upload adapters.
|
|
3
|
+
*
|
|
4
|
+
* `RetryableError` marks a thrown error as transient. `withRetry()`
|
|
5
|
+
* wraps an async function with exponential backoff plus full-jitter,
|
|
6
|
+
* abort-aware sleep, and an optional `retryAfterMs` override carried
|
|
7
|
+
* on the error.
|
|
8
|
+
*
|
|
9
|
+
* @experimental Public surface may change before v1.0. Pinned by the
|
|
10
|
+
* package's `api/api-snapshot.json` once the snapshot script is
|
|
11
|
+
* extended to scan this entrypoint.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Marks an error as transient — `withRetry()` will reschedule the
|
|
15
|
+
* underlying call rather than rethrow.
|
|
16
|
+
*
|
|
17
|
+
* Adapters should throw `RetryableError` for HTTP 5xx, network
|
|
18
|
+
* failures, and other recoverable conditions; non-retryable errors
|
|
19
|
+
* (4xx, schema mismatches, abort) should be thrown as plain `Error`
|
|
20
|
+
* subclasses (typically `AssetValidationError`).
|
|
21
|
+
*
|
|
22
|
+
* The optional `retryAfterMs` overrides the next computed delay,
|
|
23
|
+
* useful when the server returned a `Retry-After` header.
|
|
24
|
+
*/
|
|
25
|
+
export declare class RetryableError extends Error {
|
|
26
|
+
readonly retryAfterMs?: number;
|
|
27
|
+
constructor(message: string, options?: {
|
|
28
|
+
readonly cause?: unknown;
|
|
29
|
+
readonly retryAfterMs?: number;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export interface RetryOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of retry attempts after the initial call.
|
|
35
|
+
* `maxRetries: 3` means up to 4 total invocations.
|
|
36
|
+
*
|
|
37
|
+
* @default 3
|
|
38
|
+
*/
|
|
39
|
+
readonly maxRetries?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Base delay in milliseconds. The actual delay grows exponentially
|
|
42
|
+
* (`baseDelayMs * 2^attempt`) and is then jittered.
|
|
43
|
+
*
|
|
44
|
+
* @default 250
|
|
45
|
+
*/
|
|
46
|
+
readonly baseDelayMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Cap on the computed backoff delay (before jitter).
|
|
49
|
+
*
|
|
50
|
+
* @default 8000
|
|
51
|
+
*/
|
|
52
|
+
readonly maxDelayMs?: number;
|
|
53
|
+
/** Aborts both the in-flight call and any pending retry sleep. */
|
|
54
|
+
readonly signal?: AbortSignal;
|
|
55
|
+
/** Defaults to `Math.random`. Override for deterministic tests. */
|
|
56
|
+
readonly jitter?: () => number;
|
|
57
|
+
/** Defaults to a `setTimeout`-based, abort-aware sleep. */
|
|
58
|
+
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Run `fn` and retry on `RetryableError` with full-jitter exponential
|
|
62
|
+
* backoff. Honors `signal` between attempts and during sleep.
|
|
63
|
+
*
|
|
64
|
+
* Resolution order on each error:
|
|
65
|
+
* 1. If the signal is aborted → throw `AbortError` immediately.
|
|
66
|
+
* 2. If the error is not a `RetryableError` → rethrow.
|
|
67
|
+
* 3. If retries are exhausted → rethrow the last error.
|
|
68
|
+
* 4. Otherwise compute backoff, sleep, and try again.
|
|
69
|
+
*/
|
|
70
|
+
export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
71
|
+
//# sourceMappingURL=retry.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.cts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;GAWG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAG9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;CAYvE;AAED,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAMD;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAChC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,GAAE,YAAiB,GACxB,OAAO,CAAC,CAAC,CAAC,CAmCZ"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Retry helpers used by network-bound upload adapters.
|
|
3
|
+
*
|
|
4
|
+
* `RetryableError` marks a thrown error as transient. `withRetry()`
|
|
5
|
+
* wraps an async function with exponential backoff plus full-jitter,
|
|
6
|
+
* abort-aware sleep, and an optional `retryAfterMs` override carried
|
|
7
|
+
* on the error.
|
|
8
|
+
*
|
|
9
|
+
* @experimental Public surface may change before v1.0. Pinned by the
|
|
10
|
+
* package's `api/api-snapshot.json` once the snapshot script is
|
|
11
|
+
* extended to scan this entrypoint.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Marks an error as transient — `withRetry()` will reschedule the
|
|
15
|
+
* underlying call rather than rethrow.
|
|
16
|
+
*
|
|
17
|
+
* Adapters should throw `RetryableError` for HTTP 5xx, network
|
|
18
|
+
* failures, and other recoverable conditions; non-retryable errors
|
|
19
|
+
* (4xx, schema mismatches, abort) should be thrown as plain `Error`
|
|
20
|
+
* subclasses (typically `AssetValidationError`).
|
|
21
|
+
*
|
|
22
|
+
* The optional `retryAfterMs` overrides the next computed delay,
|
|
23
|
+
* useful when the server returned a `Retry-After` header.
|
|
24
|
+
*/
|
|
25
|
+
export declare class RetryableError extends Error {
|
|
26
|
+
readonly retryAfterMs?: number;
|
|
27
|
+
constructor(message: string, options?: {
|
|
28
|
+
readonly cause?: unknown;
|
|
29
|
+
readonly retryAfterMs?: number;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export interface RetryOptions {
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of retry attempts after the initial call.
|
|
35
|
+
* `maxRetries: 3` means up to 4 total invocations.
|
|
36
|
+
*
|
|
37
|
+
* @default 3
|
|
38
|
+
*/
|
|
39
|
+
readonly maxRetries?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Base delay in milliseconds. The actual delay grows exponentially
|
|
42
|
+
* (`baseDelayMs * 2^attempt`) and is then jittered.
|
|
43
|
+
*
|
|
44
|
+
* @default 250
|
|
45
|
+
*/
|
|
46
|
+
readonly baseDelayMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Cap on the computed backoff delay (before jitter).
|
|
49
|
+
*
|
|
50
|
+
* @default 8000
|
|
51
|
+
*/
|
|
52
|
+
readonly maxDelayMs?: number;
|
|
53
|
+
/** Aborts both the in-flight call and any pending retry sleep. */
|
|
54
|
+
readonly signal?: AbortSignal;
|
|
55
|
+
/** Defaults to `Math.random`. Override for deterministic tests. */
|
|
56
|
+
readonly jitter?: () => number;
|
|
57
|
+
/** Defaults to a `setTimeout`-based, abort-aware sleep. */
|
|
58
|
+
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Run `fn` and retry on `RetryableError` with full-jitter exponential
|
|
62
|
+
* backoff. Honors `signal` between attempts and during sleep.
|
|
63
|
+
*
|
|
64
|
+
* Resolution order on each error:
|
|
65
|
+
* 1. If the signal is aborted → throw `AbortError` immediately.
|
|
66
|
+
* 2. If the error is not a `RetryableError` → rethrow.
|
|
67
|
+
* 3. If retries are exhausted → rethrow the last error.
|
|
68
|
+
* 4. Otherwise compute backoff, sleep, and try again.
|
|
69
|
+
*/
|
|
70
|
+
export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
71
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;GAWG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAG9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;CAYvE;AAED,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAMD;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAChC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,GAAE,YAAiB,GACxB,OAAO,CAAC,CAAC,CAAC,CAmCZ"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
class RetryableError extends Error {
|
|
2
|
+
retryAfterMs;
|
|
3
|
+
constructor(message, options){
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "RetryableError";
|
|
6
|
+
if (options && "cause" in options) this.cause = options.cause;
|
|
7
|
+
if (options?.retryAfterMs !== void 0) this.retryAfterMs = options.retryAfterMs;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
11
|
+
const DEFAULT_BASE_DELAY_MS = 250;
|
|
12
|
+
const DEFAULT_MAX_DELAY_MS = 8000;
|
|
13
|
+
async function withRetry(fn, options = {}) {
|
|
14
|
+
const { maxRetries = DEFAULT_MAX_RETRIES, baseDelayMs = DEFAULT_BASE_DELAY_MS, maxDelayMs = DEFAULT_MAX_DELAY_MS, signal, jitter = Math.random, sleep = defaultSleep } = options;
|
|
15
|
+
let attempt = 0;
|
|
16
|
+
while(true){
|
|
17
|
+
throwIfAborted(signal);
|
|
18
|
+
try {
|
|
19
|
+
return await fn(attempt);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throwIfAborted(signal);
|
|
22
|
+
if (!isRetryable(error)) throw error;
|
|
23
|
+
if (attempt >= maxRetries) throw error;
|
|
24
|
+
const delay = computeDelay({
|
|
25
|
+
attempt,
|
|
26
|
+
baseDelayMs,
|
|
27
|
+
maxDelayMs,
|
|
28
|
+
jitter,
|
|
29
|
+
retryAfterMs: getRetryAfterMs(error)
|
|
30
|
+
});
|
|
31
|
+
await sleep(delay, signal);
|
|
32
|
+
attempt += 1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function isRetryable(error) {
|
|
37
|
+
if (error instanceof RetryableError) return true;
|
|
38
|
+
return null !== error && "object" == typeof error && "RetryableError" === error.name;
|
|
39
|
+
}
|
|
40
|
+
function getRetryAfterMs(error) {
|
|
41
|
+
if (null !== error && "object" == typeof error && "number" == typeof error.retryAfterMs) return error.retryAfterMs;
|
|
42
|
+
}
|
|
43
|
+
function computeDelay(input) {
|
|
44
|
+
if (void 0 !== input.retryAfterMs) return Math.max(0, input.retryAfterMs);
|
|
45
|
+
const exp = Math.min(input.maxDelayMs, input.baseDelayMs * 2 ** input.attempt);
|
|
46
|
+
const half = exp / 2;
|
|
47
|
+
return Math.floor(half + input.jitter() * half);
|
|
48
|
+
}
|
|
49
|
+
function throwIfAborted(signal) {
|
|
50
|
+
if (signal?.aborted) throw makeAbortError(signal);
|
|
51
|
+
}
|
|
52
|
+
function makeAbortError(signal) {
|
|
53
|
+
const reason = signal.reason;
|
|
54
|
+
if (reason instanceof Error) return reason;
|
|
55
|
+
if ("u" > typeof DOMException) return new DOMException("Aborted", "AbortError");
|
|
56
|
+
const error = new Error("Aborted");
|
|
57
|
+
error.name = "AbortError";
|
|
58
|
+
return error;
|
|
59
|
+
}
|
|
60
|
+
function defaultSleep(ms, signal) {
|
|
61
|
+
if (ms <= 0) {
|
|
62
|
+
throwIfAborted(signal);
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve, reject)=>{
|
|
66
|
+
const timer = setTimeout(()=>{
|
|
67
|
+
signal?.removeEventListener("abort", onAbort);
|
|
68
|
+
resolve();
|
|
69
|
+
}, ms);
|
|
70
|
+
const onAbort = ()=>{
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
reject(makeAbortError(signal));
|
|
73
|
+
};
|
|
74
|
+
if (signal) {
|
|
75
|
+
if (signal.aborted) {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
reject(makeAbortError(signal));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
signal.addEventListener("abort", onAbort, {
|
|
81
|
+
once: true
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export { RetryableError, withRetry };
|